#---------------------------------------------------------------------------
# Author : Manasa Ramakrishna, mr325@le.ac.uk
# Date started : 4th August, 2017
# Last modified : 4th August, 2017
# Aim : To take a look at first SILAC labelled LOPIT data on Trizol
# Depends : On 'silacFunctions.R'. Make sure they are in the same directory
# Notes : Works on data from Rayner's first experiments
#---------------------------------------------------------------------------
# Invoking libraries
library(MSnbase)
library(gplots)
library(reshape2)
library(VIM)
library(zoo)
library(spatstat) # "im" function
library(ggbiplot)
library(org.Hs.eg.db)
library(clusterProfiler)
library("biomaRt")
library(goseq)
library(limma)
library(ggplot2)
library(outliers)
library(RColorBrewer)
library(stringr)
#Setting working directories
wd = "/Users/manasa/Documents/Work/TTT/02_Proteomics/02_First-TMT-data/"
setwd(wd)
getwd()
[1] "/Users/manasa/Documents/Work/TTT/02_Proteomics/02_First-TMT-data"
indir = paste(wd,"Input",sep="/")
outdir = paste(wd,paste(Sys.Date(),"Output",sep = "_"),sep = "/")
if (exists(outdir)){
print("Outdir exists")
}else{
dir.create(outdir)
}
# Sourcing function file
source("tmtFunctions.R")
Now that we have loaded all the packages we need for working with this data, let’s move on to the data.
# -----------------------------------------------
# Step 00: Read data
# Read in all the data required for analysis
# -----------------------------------------------
# File of contaminants - proteins to exclude from analysis as are things like keratin, alcohol dehydrogenase etc....
contam = read.delim("Input/Common contaminant_all.csv",sep=",",header=T)
# Read in the sample file that matches columns to sample contents
samp.dat = read.delim("Input/samples.txt",sep="\t",header=T)
# Read in the data files that contain peptide level output from Proteome discoverer...
# Note: The columns that begin with "Found.in.Sample.in" correspond to various samples in the study.
# Columns of interest are "sequence", "modifications","master.protein.accessions","abundance","quan.info"
data = read.delim("Input/Dosages_4step-Trizol_PeptideGroups.txt",sep="\t",comment.char="",as.is=T,header=T)
# Subset data to only keep columns of interest
prot.data = data[,c(1:6,10:14,17,42:51,62)]
# Rename tmt tagged columns with UV dosage names
for(i in 1:nrow(samp.dat)){
id = grep(samp.dat$TMT[i],colnames(prot.data))
colnames(prot.data)[id] = paste(samp.dat$Sample[i])
}
dim(prot.data)
[1] 14456 23
head(prot.data)
data has 23 columns and 14456 rows - each row belonging to a peptide abundance value across all 10 samples. We now go through a series of filtering steps to obtain a dataset we can use for downstream analyses.
# ---------------------------------------------------------------------------------
# Step 01 : Filter
# We perform 3 layers of filtering - unique proteins, contaminants,missing values
# ---------------------------------------------------------------------------------
# Step 1a : Filter only for those peptides that have a unique master protein. Done using column "quan.info" and titled 'Unique'
peptide.stats = table(prot.data$Quan.Info)
peptide.stats
NoQuanValues NotUnique Unique
1440 715 12301
filt.1a = prot.data[which(prot.data$Quan.Info == "Unique"),]
dim(filt.1a) #12301 are unique peptides, 715 are non-unique and 1440 are missing values
[1] 12301 23
# Step 1b : Filter out those proteins that are contaminants from the contaminants list and annotate missing values
filt.1b = filt.1a[-which(filt.1a$Master.Protein.Accessions %in% contam$Protein.Group.Accessions),]
num.contams = length(which(filt.1a$Master.Protein.Accessions %in% contam$Protein.Group.Accessions))
dim(filt.1a) # 12301 in total
[1] 12301 23
dim(filt.1b) # 12077 filtered proteins
[1] 12077 23
print(num.contams) # 224 contaminant proteins
[1] 224
# Adding extra information about rows with missing values
filt.1b$count.missing = rowSums(is.na(filt.1b[,c(13:22)]))
filt.1b$Missing = FALSE
filt.1b$Missing[which(filt.1b$count.missing > 0)] = TRUE
head(filt.1b)
We have a column called “Missing” to identify which peptides have one or more missing values across the 10 samples. “count.missing” tells us how many missing values there are for that peptide.
# ---------------------------------------------------------------------------------
# Step 02a : Creating an MSnSet which is needed for using the MSnbase backage
# ---------------------------------------------------------------------------------
# The rownames of samp.dat have to be the same as column names in the expression data matrix
rownames(samp.dat) = samp.dat$Sample
# Create an MSnSet object
res <- MSnSet(exprs = as.matrix(filt.1b[,c(13:22)]),fData=filt.1b[,c(10,1:9,12,23:25)],pData = samp.dat[,c(2,1)])
res <- res[rowSums(is.na(exprs(res)))!=10,] # exclude peptides without any quantification
print(res)
MSnSet (storageMode: lockedEnvironment)
assayData: 12077 features, 10 samples
element names: exprs
protocolData: none
phenoData
sampleNames: 150mJ.1 150mJ.2 ... Pool.1 (10 total)
varLabels: TMT Sample
varMetadata: labelDescription
featureData
featureNames: 2 3 ... 14455 (12077 total)
fvarLabels: Master.Protein.Accessions Checked ... Missing (14 total)
fvarMetadata: labelDescription
experimentData: use 'experimentData(object)'
Annotation:
- - - Processing information - - -
Subset [12077,10][12077,10] Wed Sep 13 14:19:33 2017
MSnbase version: 2.0.2
# How many missing values per peptide
table(fData(res)$count.missing)
0 1 2 3 4 5 6 7 8 9
11451 551 38 16 8 4 2 4 1 2
colSums(is.na(exprs(res)))
150mJ.1 150mJ.2 150mJ.3 275mJ.1 275mJ.2 275mJ.3 400mJ.1 400mJ.2 400mJ.3 Pool.1
22 11 612 49 6 17 6 33 6 31
table(rowSums(is.na(exprs(res))))
0 1 2 3 4 5 6 7 8 9
11451 551 38 16 8 4 2 4 1 2
# Checking missing values
table(is.na(res))
FALSE TRUE
119977 793
The MSnSet object has been created to include protein abundance values, some metadata and sample information (UV dosage in this case)
# ---------------------------------------------------------------------------------
# Step 02b : Imputing missing values in the protein expression data using 'impute'
# ---------------------------------------------------------------------------------
# Subsetting only those peptides with one or more missing values
# Replacing missing values with 0 and non-missing with 1
# Displaying missing values
miss.many = res[rowSums(is.na(exprs(res)))>=1,]
exprs(miss.many)[exprs(miss.many) != 0] = 1
exprs(miss.many)[is.na(exprs(miss.many))] = 0
heatmap.2(exprs(miss.many), col = c("lightgray", "black"),
scale = "none", dendrogram = "none",
trace = "none", keysize = 0.5, key = FALSE,Colv=F,
ColSideColors = rep(c("steelblue", "darkolivegreen","magenta","black"), times = c(3,3,3,1)))

# Impute missing values
impute.res <- impute(res,method = "knn")
9 rows with more than 50 % entries missing;
mean imputation used for these rows
Cluster size 12068 broken into 11832 236
Cluster size 11832 broken into 8982 2850
Cluster size 8982 broken into 3383 5599
Cluster size 3383 broken into 1164 2219
Done cluster 1164
Cluster size 2219 broken into 1271 948
Done cluster 1271
Done cluster 948
Done cluster 2219
Done cluster 3383
Cluster size 5599 broken into 3042 2557
Cluster size 3042 broken into 1360 1682
Done cluster 1360
Cluster size 1682 broken into 631 1051
Done cluster 631
Done cluster 1051
Done cluster 1682
Done cluster 3042
Cluster size 2557 broken into 1453 1104
Done cluster 1453
Done cluster 1104
Done cluster 2557
Done cluster 5599
Done cluster 8982
Cluster size 2850 broken into 725 2125
Done cluster 725
Cluster size 2125 broken into 1238 887
Done cluster 1238
Done cluster 887
Done cluster 2125
Done cluster 2850
Done cluster 11832
Done cluster 236
pData(impute.res)$Sample = gsub('\\s+','',pData(impute.res)$Sample)
# Plot imputed values
res.miss = melt(exprs(res))
colnames(res.miss) = c("Row","Dosage","Abundance_imp")
res.no.miss = melt(exprs(impute.res))
colnames(res.no.miss) = c("Row","Dosage","Abundance")
imp.vals = res.no.miss[which(is.na(res.miss$Abundance_imp)),]
# Some boxplots of the data
#---------------------------
# All data including missing values
boxplot(log2(res.miss$Abundance)~as.factor(res.miss$Dosage),las=2,col=rep(c("turquoise", "salmon","palegreen","plum1"),times=c(3,3,3,1)),main="All data including missing values")

# Imputed values/missing values only
b.imp = boxplot(log2(imp.vals$Abundance)~imp.vals$Dosage,las=2,col=rep(c("turquoise", "salmon","palegreen","plum1"),times=c(3,3,3,1)),main="Imputed values only")

boxplot(log2(imp.vals$Abundance)~imp.vals$Dosage,las=2,names = paste(b.imp$names," (n=", b.imp$n, ")",sep=""),col=rep(c("turquoise", "salmon","palegreen","plum1"),times=c(3,3,3,1)),main="Imputed values only",,cex.axis = 0.6)

# All data icluding newly imputed values
boxplot(log2(res.no.miss$Abundance)~as.factor(res.no.miss$Dosage),las=2,col=rep(c("turquoise", "salmon","palegreen","plum1"),times=c(3,3,3,1)),main="All data with imputed values")

# Additional plots showing fraction of missing values
# Not much use as in our case, it is very small.
vim.dat = data.frame(cbind(Abundance_imp=res.miss$Abundance_imp,Abundance=res.no.miss$Abundance),stringsAsFactors = F)
head(vim.dat)
vim.dat$which.miss = FALSE
vim.dat$which.miss[which(is.na(vim.dat$Abundance_imp))] = TRUE
table(vim.dat$which.miss)
FALSE TRUE
119977 793
histMiss(vim.dat, only.miss = F)
Click in in the left margin to switch to the previous variable or in the right margin to switch to the next variable.
To regain use of the VIM GUI and the R console, click anywhere else in the graphics window.

pbox(vim.dat, ylim=c(0,500),selection="none")
Click in in the left margin to switch to the previous variable or in the right margin to switch to the next variable.
To regain use of the VIM GUI and the R console, click anywhere else in the graphics window.

matrixplot(vim.dat)
Click in a column to sort by the corresponding variable.
To regain use of the VIM GUI and the R console, click outside the plot region.

We have a small number of missing values - 793 peptides have one or more missing values out of 12244 peptides in total. 551/793 are missing in 1 sample only and 38/793 in 2 samples. Only 2 peptides are missing in 9/10 samples.
# ---------------------------------------------------------------------------------
# Step 03 : Normalising imputed data using various methods to determine ideal one
# ---------------------------------------------------------------------------------
qnt.max <- normalise(impute.res, "max")
qnt.sum <- normalise(impute.res, "sum")
qnt.quant <- normalise(impute.res, "quantiles")
qnt.qrob <- normalise(impute.res, "quantiles.robust")
qnt.vsn <- normalise(impute.res, "vsn")
vsn2: 12077 x 10 matrix (1 stratum). Please use 'meanSdPlot' to verify the fit.
## ---- plotting function---------
.plot <- function(x,ttl=NULL) {
boxplot(exprs(x),
main=ifelse(is.null(ttl),processingData(x)@processing[2],ttl),
cex.main=1.5,
cex.lab=.5,
cex.axis=0.8,
cex=.8,
las=2)
grid()
}
# Using the plotting function to plot boxplots for all diff types of normalisation methods
oldmar <- par()$mar
par(mfrow=c(3,2),mar=c(2.9,2.9,2.9,1))
.plot(impute.res, ttl = "Non-normalised data")
.plot(qnt.max, ttl = "Maximum")
.plot(qnt.sum, ttl = "Sum")
.plot(qnt.quant, ttl = "Quantile")
.plot(qnt.qrob, ttl = "Robust quantile")
.plot(qnt.vsn, ttl = "vsn")

# Checking the effects of normalisation on covariance
sd1 <- apply(log2(exprs(impute.res))+10,1,sd)
mn1 <- apply(log2(exprs(impute.res))+10,1,mean)
cv1 <- sd1/mn1
sd2 <- apply(exprs(qnt.vsn)+10,1,sd)
mn2 <- apply(exprs(qnt.vsn)+10,1,mean)
cv2 <- sd2/mn2
dfr <- rbind(data.frame(rank=order(mn1),cv=cv1,norm="raw"),
data.frame(rank=order(mn2),cv=cv2,norm="vsn"))
rmed1 <- rollapply(cv1,7,function(x) median(x,na.rm=TRUE))
rmed2 <- rollapply(cv2,7,function(x) median(x,na.rm=TRUE))
dfr2 <- rbind(data.frame(x=seq(0,30,by=30/length(rmed1))[-1],y=rmed1,norm="raw"),data.frame(x=seq(0,30,by=30/length(rmed2))[-1],y=rmed2,norm="vsn"))
p <- ggplot()+geom_line(data=dfr2,aes(x=x,y=y,col=norm)) + theme_gray(7)
plot(p)

Will keep the data from the vsn normalisation for downstream analyses as it normalises the data better than other methods used in this comparison such as “sum”, “max”, “quantile”.
For all further steps, we will use the object “qnt.vsn”
# ---------------------------------------------------------------------------------
# Step 04a : Aggregate peptide data to protein expression values
# There is an in-built function called 'combineFeatures' to do thi within MSnBase
# ---------------------------------------------------------------------------------
protnames <- fData(qnt.vsn)$Master.Protein.Accessions
#table(protnames)
length(unique(protnames)) # 1744 proteins present in all 10 samples
[1] 1744
# Aggregating peptide abundance values into protein abundance values
qnt.prot <- combineFeatures(qnt.vsn, groupBy = protnames, fun = "median")
Combined 12077 features into 12077 using median
dim(qnt.prot)
[1] 1744 10
head(exprs(qnt.prot))
150mJ.1 150mJ.2 150mJ.3 275mJ.1 275mJ.2 275mJ.3 400mJ.1 400mJ.2 400mJ.3 Pool.1
A0A024QZP7 3.816467 3.620175 3.927225 4.052191 3.357771 3.310799 3.357685 3.268323 3.294870 3.651255
A0A024R216 7.599068 8.060989 6.553413 5.027294 7.777270 7.677502 7.354644 7.367304 7.238476 7.079061
A0A024R4E5 6.329437 5.803502 6.730733 6.286203 6.320219 6.172760 5.830347 5.964010 5.750300 6.287400
A0A024R644 5.396222 5.055941 4.600265 4.333401 4.729008 4.757164 4.156337 4.452842 4.393996 4.887391
A0A075B716 6.043305 5.677170 5.677677 5.247188 5.606109 5.426848 5.970442 5.801846 5.696659 5.619219
A0A087WSV8 4.562525 5.022626 4.362861 4.670520 4.962158 5.500905 6.268788 5.923221 5.913728 4.863713
head(exprs(qnt.vsn))
150mJ.1 150mJ.2 150mJ.3 275mJ.1 275mJ.2 275mJ.3 400mJ.1 400mJ.2 400mJ.3 Pool.1
2 2.768493 1.966696 6.596663 6.509413 2.401781 6.540405 1.903026 6.520467 6.507783 6.511016
3 4.166554 4.035286 5.620443 6.395213 4.589417 5.245986 6.144995 5.496511 5.716597 5.117277
4 5.200775 5.765512 5.219895 5.058563 5.142504 5.314060 4.817990 4.729333 4.899958 4.730216
5 5.892083 5.608025 5.671977 5.279402 5.237583 4.981204 5.499543 5.309686 5.272549 5.135761
6 5.455946 5.232610 4.995591 5.433890 5.079733 4.986121 5.175707 5.402227 5.270593 5.470324
7 2.355212 1.760496 4.276767 3.345973 1.786217 1.852261 2.003715 2.348329 1.819560 2.025044
# Basic plots of protein data across samples
.plot(qnt.prot,ttl="Aggregated-proteins")

plot(hclust(dist(exprs(t(qnt.prot)))))

# Looking at sample correlations
cor.prot = cor(exprs(qnt.prot))
heatmap(cor.prot,cex.main = 0.8)

dissimilarity <- 1 - cor.prot
distance <- as.dist(dissimilarity)
plot(hclust(distance))

pairs(x = exprs(qnt.prot), upper.panel=NULL, pch=20)

# Using the "duplicatecorrelation" function in limma to test correlation between technical replicates
# If Rayner's ordering is correct, then the samples are
biolrep.rq <- c(1,1,1,2,2,2,3,3,3,4)
# If there is a swap between Pool.1 and 275mj.rep1, then it should be
biolrep.swap <- c(1,1,1,4,2,2,3,3,3,2)
# If it is indeed a swap, then the correlation should increase when corrected. It does!
rq.cor = duplicateCorrelation(qnt.prot,ndups=1,block=biolrep.rq)$consensus.correlation # 0.203
swap.cor = duplicateCorrelation(qnt.prot,ndups=1,block=biolrep.swap)$consensus.correlation # 0.564
rq.cor
[1] 0.2031868
swap.cor
[1] 0.5641893
cor.prot
150mJ.1 150mJ.2 150mJ.3 275mJ.1 275mJ.2 275mJ.3 400mJ.1 400mJ.2 400mJ.3 Pool.1
150mJ.1 1.0000000 0.9335052 0.7911591 0.4153498 0.9280369 0.8853188 0.6821358 0.7285274 0.6684451 0.8458091
150mJ.2 0.9335052 1.0000000 0.7551314 0.4370340 0.9500877 0.9236256 0.7780666 0.8027901 0.7780055 0.8816818
150mJ.3 0.7911591 0.7551314 1.0000000 0.5989892 0.7830856 0.7672976 0.6600698 0.6912729 0.6334646 0.7386629
275mJ.1 0.4153498 0.4370340 0.5989892 1.0000000 0.5515125 0.5783829 0.6087879 0.6728370 0.6634102 0.6850855
275mJ.2 0.9280369 0.9500877 0.7830856 0.5515125 1.0000000 0.9663206 0.8269695 0.8625385 0.8276158 0.9248869
275mJ.3 0.8853188 0.9236256 0.7672976 0.5783829 0.9663206 1.0000000 0.8494414 0.8778792 0.8443968 0.9432591
400mJ.1 0.6821358 0.7780666 0.6600698 0.6087879 0.8269695 0.8494414 1.0000000 0.9486792 0.9224763 0.8623312
400mJ.2 0.7285274 0.8027901 0.6912729 0.6728370 0.8625385 0.8778792 0.9486792 1.0000000 0.9264799 0.9100994
400mJ.3 0.6684451 0.7780055 0.6334646 0.6634102 0.8276158 0.8443968 0.9224763 0.9264799 1.0000000 0.8713971
Pool.1 0.8458091 0.8816818 0.7386629 0.6850855 0.9248869 0.9432591 0.8623312 0.9100994 0.8713971 1.0000000
#duplicateCorrelation(qnt.prot[,1:9],ndups=1,block=c(1,1,1,2,2,2,3,3,3))$consensus.correlation # 0.23
#duplicateCorrelation(qnt.prot[,c(1:3,5:10)],ndups=1,block=c(1,1,1,2,2,3,3,3,2))$consensus.correlation # 0.37
We have merged the peptides into proteins and are looking here at the correlations within replicates of UV dosage. Replicate 3 of 150mJ dosage is a bit off from the other two while replicate 1 of 275mJ sits by itself allowing the “Pool” sample to cluster with the other 275mJ samples.
I thought that 275mJ.3 and Pool might have been swapped. So the various plots were done to prove whether or not this was true. The “duplicateCorrelation” function yields a much higher correlation when we assume that 275mJ.3 is swapped with Pool.1 than if we didn’t.
Looking at th correlation values, 275.mJ vs Pool is more correlated than 150mJ vs Pool and 400mJ vs Pool. This could be becuase there was more material from sample 275mJ going into the pool. Rayner used the same volume of each of the 9 samples rather than same amount in the pool. Hence the higher correlation (well at least it is my best guess).
# ---------------------------------------------------------------------------------
# Step 04a : Plotting PCAs
# This is to look at variability across samples and within replicates
# ---------------------------------------------------------------------------------
# Across all 10 samples
prot.pca = prcomp(t(exprs(qnt.prot)),scale=T)
j <- ggbiplot(prot.pca, var.axes=F, groups = factor(c(1,1,1,2,2,2,3,3,3,4)), circle = T,obs.scale=1,labels=rownames(prot.pca$x))
print(j)

# Excluding the Pool.1 sample
prot.pca.rq = prcomp(t(exprs(qnt.prot[,c(1:9)])),scale=T)
g <- ggbiplot(prot.pca.rq, var.axes=F, groups = factor(c(1,1,1,2,2,2,3,3,3)), circle = T,obs.scale=1,labels=rownames(prot.pca.rq$x))
g <- g+labs(colour = "UV Dosage")
print(g)

# Assuming Pool.1 is actually 275mJ.rep1
prot.pca.swap = prcomp(t(exprs(qnt.prot[,c(1:3,5:10)])),scale=T)
h <- ggbiplot(prot.pca.swap, var.axes=F, groups = factor(c(1,1,1,2,2,3,3,3,2)), circle = T,obs.scale=1,labels=rownames(prot.pca.swap$x))
print(h)

# Removing both problem samples
prot.pca.no.dud = prcomp(t(exprs(qnt.prot[,c(1:2,5:10)])),scale=T)
k <- ggbiplot(prot.pca.no.dud, var.axes=F, groups = factor(c(1,1,2,2,3,3,3,4)), circle = T,obs.scale=1,labels=rownames(prot.pca.no.dud$x))
print(k)

# To finish off, some boxplots....
pData(qnt.prot) = pData(impute.res)
# Are the values vastly different
boxplot(exprs(qnt.prot),las=2)

I’ve been looking to explain what I see. It seems like the most obvious answer would be that Pool.1 and 275mJ.1 are swapped. However, upon speaking with Rayner, this did not happen. He sets up the tubes in a row and adds the labels in order so the last sample was the pool and the 4th was 275mJ.1. What Rayner did say was that the tube with 150mJ.3 was dropped and sample had a little shake/tumble which might have affected its quality. I don’t know at which stage of the protocol this happened but it did. This would explain the separation of 150mJ.3 from the other two 150mJs. However, the 150mJ triplicate still cluster together on the hclust plots. With sample 275mJ.1, Rayner remembers it being odd but we don’t know why or how.
An alternate explanation is that sample 275mJ.1 did not elute(?) out properly while it was being prepared but it did by the time the pool was made. The pool was made of equal volumes of all 9 samples rather than equal protein amounts so there will be a higher representation of more abundant samples and lower of less abundant samples.
Have gone back to look at the “cor.prot” matrix and as Tom suspected, the Pool.1 correlates slightly better with non-dodgey 275mJ (corr = 0.94) than with 400mJ (0.88) and non-dodgey 150mJ (0.87) but not significantly more, hence the cluster plot looks like it does. The two dogey samples 150mJ.3 and 275mJ.1 have been left out of the average correlation calculation above.
Would like to take a picture of the table of final concentrations of samples that went into the pool and put it in the pData dataframe so we have it for furture reference. Rayner is re-doing experiment.
The boxplots seem to indicate that the two dodgey samples are missing proteins that are lowly expressed. Maybe they were too low to be detected by the mass spec ?
# -------------------------------------------------------------------------------------------------------------------------------------
# Step 05: Creating a background list of proteins for U20S to be used for functional enrichment
# Using GO terms and Interpro domains for function of proteins detected by UV crosslinking and Trizol-enrichment
# All data are based on Trizol-4-step and 400mJ of UV exposure for 2 mins (?)
# -------------------------------------------------------------------------------------------------------------------------------------
#==========================================================================================================================================
# Using U2OS protein list from Geiger et al as the background list. This consists of maximal list of proteins expressed in U20S cell lines
# ~ 7500 proteins. We are however reading in the peptides and aggregating them using MSnbase as we did with the UV dosage data
#==========================================================================================================================================
# Read data from Tom's list of Geiger proteins
# Tom took the Supplementary data from paper and re-annotated it with master proteins using his script.
# The script gets focuses on using Swissprot IDs rather than both Swissprot and Trembl.
# It also marks which proteins are "crap" or "contaminants"
# Finally, it provides a measure of which peptides could be mapped to a unique protein. We use this as a filter for downstream analyses
u2os <- read.delim("Input/Geiger_et_al_2012_U2OS_peptides_plus_master.txt",sep="\t",header=T)
head(u2os)
dim(u2os) # 68621 21
[1] 68621 21
# Filter data - keep peptides with unique master proteins, those which are not "crap" and those that are not missing a master protein
u2os.filt1 = u2os[which(u2os$unique == 1 & u2os$master_protein != "" & u2os$crap_protein != 1),]
# Keep only those columnns with data of interest
# Geiger et al produced a triplicate MS dataset for the U2OS cell line
u2os.filt2 = u2os.filt1[,c(5:6,17,11:16,18:19)]
head(u2os.filt2)
# Checking for peptide loss. We loose ~ 400 peptides out of nearly 70,000 so not too concerned
dim(u2os) # 68621 21
[1] 68621 21
dim(u2os.filt1) # 64967 21
[1] 64967 21
dim(u2os.filt2) # 64967 11
[1] 64967 11
# Create an MSnSet object of the background list
u2os.samp = data.frame(sample=c("Intensity.U2OS_1","Intensity.U2OS_2","Intensity.U2OS_3"),rep=c(1,2,3))
rownames(u2os.samp) = u2os.samp$sample
res.u2os <- MSnSet(exprs = as.matrix(u2os.filt2[,c(5:7)]),fData=u2os.filt2[,-c(5:7)],pData = u2os.samp)
# Impute missing data in the background list from Geiger et al.
# Haven't drawn any plots for this. Can do it at a later date
impute.u2os <- impute(res.u2os,method = "knn")
28533 rows with more than 50 % entries missing;
mean imputation used for these rows
Cluster size 36434 broken into 28537 7897
Cluster size 28537 broken into 7812 20725
Cluster size 7812 broken into 4633 3179
Cluster size 4633 broken into 3045 1588
Cluster size 3045 broken into 1231 1814
Done cluster 1231
Cluster size 1814 broken into 860 954
Done cluster 860
Done cluster 954
Done cluster 1814
Done cluster 3045
Cluster size 1588 broken into 758 830
Done cluster 758
Done cluster 830
Done cluster 1588
Done cluster 4633
Cluster size 3179 broken into 1044 2135
Done cluster 1044
Cluster size 2135 broken into 1220 915
Done cluster 1220
Done cluster 915
Done cluster 2135
Done cluster 3179
Done cluster 7812
Cluster size 20725 broken into 18245 2480
Cluster size 18245 broken into 9841 8404
Cluster size 9841 broken into 3640 6201
Cluster size 3640 broken into 1877 1763
Cluster size 1877 broken into 788 1089
Done cluster 788
Done cluster 1089
Done cluster 1877
Cluster size 1763 broken into 775 988
Done cluster 775
Done cluster 988
Done cluster 1763
Done cluster 3640
Cluster size 6201 broken into 3330 2871
Cluster size 3330 broken into 2482 848
Cluster size 2482 broken into 1250 1232
Done cluster 1250
Done cluster 1232
Done cluster 2482
Done cluster 848
Done cluster 3330
Cluster size 2871 broken into 1561 1310
Cluster size 1561 broken into 350 1211
Done cluster 350
Done cluster 1211
Done cluster 1561
Done cluster 1310
Done cluster 2871
Done cluster 6201
Done cluster 9841
Cluster size 8404 broken into 5706 2698
Cluster size 5706 broken into 2228 3478
Cluster size 2228 broken into 1013 1215
Done cluster 1013
Done cluster 1215
Done cluster 2228
Cluster size 3478 broken into 1269 2209
Done cluster 1269
Cluster size 2209 broken into 1207 1002
Done cluster 1207
Done cluster 1002
Done cluster 2209
Done cluster 3478
Done cluster 5706
Cluster size 2698 broken into 1450 1248
Done cluster 1450
Done cluster 1248
Done cluster 2698
Done cluster 8404
Done cluster 18245
Cluster size 2480 broken into 1298 1182
Done cluster 1298
Done cluster 1182
Done cluster 2480
Done cluster 20725
Done cluster 28537
Cluster size 7897 broken into 4366 3531
Cluster size 4366 broken into 1923 2443
Cluster size 1923 broken into 1388 535
Done cluster 1388
Done cluster 535
Done cluster 1923
Cluster size 2443 broken into 1369 1074
Done cluster 1369
Done cluster 1074
Done cluster 2443
Done cluster 4366
Cluster size 3531 broken into 1164 2367
Done cluster 1164
Cluster size 2367 broken into 1331 1036
Done cluster 1331
Done cluster 1036
Done cluster 2367
Done cluster 3531
Done cluster 7897
# Aggregate Geiger et al data from peptides to proteins
# 64967 peptides are aggregated to 7507 proteins
agg.u2os = combineFeatures(impute.u2os,groupBy = fData(impute.u2os)$master_protein,cv = T,fun = "median")
Combined 64967 features into 64967 using median
dim(agg.u2os)
[1] 7507 3
There are 7507 proteins in the U20S cell line, across 3 replicates, based on the Geiger et al., dataset. The next step is to annotate this gene list with the various functional categories that we want to perform enrichment analysis for.
#------------------------------------------------------------------------------------------------------------------------
# Step 06: Mapping 'background' as well as 'expressed' list of proteins to various annotations
# Used a few different packages - wanted to settle on getBM from 'biomaRt' as output in easy to use format.
# Except that it was extremely slow. Hence used 'queryMany' from mygene and will reformat data.
#------------------------------------------------------------------------------------------------------------------------
#------------------------------------------------------
# Mapping Geiger U2OS list of proteins to GO terms
#------------------------------------------------------
# Geiger list being annotated with interpro descriptions - fast as done in chunks
# 7507 uniprot IDs yield 7574 lines of data
library(mygene)
Loading required package: GenomicFeatures
Loading required package: GenomeInfoDb
Loading required package: GenomicRanges
Attaching package: ‘mygene’
The following object is masked from ‘package:biomaRt’:
getGene
geiger.qm = queryMany(fData(agg.u2os)$master_protein,scopes="uniprot",fields=c("ensembl","name","symbol","interpro","go"))
Querying chunk 1
Querying chunk 2
Querying chunk 3
Querying chunk 4
Querying chunk 5
Querying chunk 6
Querying chunk 7
Querying chunk 8
Finished
Pass returnall=TRUE to return lists of duplicate or missing query terms.
geiger.qm$domains = sapply(sapply(geiger.qm$interpro,"[[",3),function(x) paste(x,collapse="; "))
geiger.qm$go.bp = sapply(sapply(geiger.qm$go.BP,"[[",2),function(x) paste(x,collapse="; "))
geiger.qm$go.mf = sapply(sapply(geiger.qm$go.MF,"[[",2),function(x) paste(x,collapse="; "))
geiger.qm$go.cc = sapply(sapply(geiger.qm$go.CC,"[[",2),function(x) paste(x,collapse="; "))
geiger.qm$go.all = paste(geiger.qm$go.bp,geiger.qm$go.cc,geiger.qm$go.mf,sep="; ")
#head(geiger.qm)
#-------------------------------------------------------------------------
# Mapping Trizol-enriched list of proteins to gene domains from Interpro.
#-------------------------------------------------------------------------
# 1744 uniprot IDs yield 1780 lines of data
uv.qm = queryMany(fData(qnt.prot)$Master.Protein.Accessions,scopes="uniprot",fields=c("ensembl","name","symbol","interpro","go"))
Querying chunk 1
Querying chunk 2
Finished
Pass returnall=TRUE to return lists of duplicate or missing query terms.
uv.qm$domains = sapply(sapply(uv.qm$interpro,"[[",3),function(x) paste(x,collapse="; "))
uv.qm$go.bp = sapply(sapply(uv.qm$go.BP,"[[",2),function(x) paste(x,collapse="; "))
uv.qm$go.mf = sapply(sapply(uv.qm$go.MF,"[[",2),function(x) paste(x,collapse="; "))
uv.qm$go.cc = sapply(sapply(uv.qm$go.CC,"[[",2),function(x) paste(x,collapse="; "))
uv.qm$go.all = paste(uv.qm$go.bp,uv.qm$go.cc,uv.qm$go.mf,sep="; ")
#head(uv.qm)
Now that we have mapped genes to annotations, time for some enrichment analysis.
#------------------------------------------------------------------------------------------------------------------------
# Step 07: Performing enrichment analysis using 'goseq' package
# We use GO terms and Interpro domains for enrichment analysis
#------------------------------------------------------------------------------------------------------------------------
# ------------------------------------------------------------------------
# Function : runGoseq
# Aim : runs goseq on a list of genes
# Input : list of genes
# Output : enriched list of Interpro domains
# ------------------------------------------------------------------------
runGoseq <- function(genelist,bglist,bias.dat=NULL,cat.oligo){
# setting up goseq object
all.genes.comp = rep(0,nrow(bglist))
names(all.genes.comp) = rownames(bglist)
all.genes.comp[which(names(all.genes.comp) %in% unique(genelist))] = 1
table(all.genes.comp)
# Remove missing values
comp.no.missing = all.genes.comp[which(!is.na(names(all.genes.comp)))]
table(comp.no.missing)
# Running the function to calculate weights. We have no bias information as we did in UV experiment
# This is because mass spec was run in detection mode not quantitation mode.
pwf.comp = nullp(comp.no.missing,'hg19','geneSymbol', bias=bias.dat,plot.fit=TRUE)
# goseq enrichment with domains for cross-linked samples
goseq.comp = goseq(pwf.comp,gene2cat = cat.oligo)
goseq.comp$BH_over_represented_pvalue = p.adjust(goseq.comp$over_represented_pvalue,method = "BH")
goseq.comp
enriched.goseq.comp = goseq.comp[which(goseq.comp$BH_over_represented_pvalue <= 0.05),]
return(list(goseq.comp,enriched.goseq.comp))
}
#----------------------------------------------------
# Using Goseq with protein abundance as a bias....
#----------------------------------------------------
# Want to be able to use both UniProt and Gene symbols as references for bias in downstream analysis
bias.df = data.frame(protbias = rowMax(exprs(agg.u2os)),UNIPROT = fData(agg.u2os)$master_protein,SYMBOL=sapply(strsplit(as.character(fData(agg.u2os)$protein_description),"\\||\\_"),"[[",3))
head(bias.df)
# Converting Geiger et al domains data to a list with each line only having one domain
geiger.doms = data.frame(geiger.qm[,c("query","domains")])
geiger.gos = data.frame(geiger.qm[,c("query","go.all")])
library(data.table)
d.dt <- data.table(geiger.doms, key="query")
geiger.cat <- d.dt[, list(domains = unlist(strsplit(domains, "; "))), by=query]
go.dt = data.table(geiger.gos, key="query")
geiger.cat.go <- go.dt[, list(go.terms = unlist(strsplit(go.all, "; "))), by=query]
# Setting up a 'genes' vector. We've only kept those genes present in u2os as the universe
# Then we mark all those that are enriched in the analysis as genes of interest (DE if you will)
# We have 1435 genes out of 1516 that have bias data and go terms
# Running goseq with GO categories
rownames(bias.df) = bias.df$UNIPROT
uv.enrich.go = runGoseq(uv.qm$query,bias.df,bias.df$protbias,geiger.cat.go)
initial point very close to some inequality constraints

# uv.enrich.go = uv.enrich.go[order(uv.enrich.go$BH_over_represented_pvalue),]
# Running goseq with Interpro domains
uv.enrich.interpro = runGoseq(uv.qm$query,bias.df,bias.df$protbias,geiger.cat)
initial point very close to some inequality constraints

# Writing output to text files
write.table(data.frame(uv.enrich.go[[2]]),paste(outdir,"Trizol-UV-dosage_GO-enrichment.txt",sep="/"),sep="\t",row.names=F,quote=F)
write.table(data.frame(uv.enrich.interpro[[2]]),paste(outdir,"Trizol-UV-dosage_Interpro-enrichment.txt",sep="/"),sep="\t",row.names=F,quote=F)
The protein “universe” used here is the list of all proteins discovered in Geiger et al., 2012. The list of peptides was mapped to proteins by Tom and annotated with master protein identifiers, crap proteins and uniqueness. This was used as it is the most comprehensive list of U2OS proteins mapped using mass spec to date. After filtering, there were ~7500 proteins generated in the study.
The “DE list” or “enriched” list of proteins are those that were expressed in the UV dosage experiment across all 9 samples. This yields 1744 proteins of which ~1500 could be mapped to GO identifiers. I tried doing this mapping with both ‘bioMart’ and ‘MyGene’ databases available as packages within R. The latter was phenomenally faster, hence I proceeded with it.
For GO enrichment, I used the ‘goseq’ package which accounts for any bias (here abundance of protein) and then checks for enrichment. ‘goseq’ yielded 94 terms of which 40 were BP, 36 were CC and 18 were MF terms. Of the BP (Biological Process) terms, more than half were involved in RNA processing and translation.
Would like to map each of the proteins to PFAM/SMART domains to see if they are indeed RNA binding proteins…… Have mapped RNA BP domains to both Geiger and UV.dosage. Need to look at the genes involved for which we have further evidence that they are indeed RBPs
The domain “Nucleotide-bd_a/b_plait” is present in almost all (16/18) heterogeneous nuclear ribonucleoproteins whose main task is to move mRNA out of the nucleus. This domain has an interpro entry IPR012677 which is no longer valid. This domain is also present in nucleolin and EWSR1. This might be the “RGG-box” domain eluded to in Burd and Dreyfuss, 1994. Excitingly, “Nucleotide-bd_a/b_plait” is a top domain the goseq analysis. I will substitute hnRNP searches with this term.
“Ig-like”/“Ig_sub” from Tom’s notes are indicative of glycoproteins which might be RNA-binding. There are 58 Ig term related proteins in the UV-dosage experiments of which only 2 have RNA-binding domains indicating only a small fraction have RNA binding capapbilities but majority of them are involved in other functions.
length(cl.and.nc.prot) # 163 present in both crosslinked and non-crosslinked samples
[1] 163
In this first step, we are reading in oligodT data from one experiment with both crosslinked(cl) and non-crosslinked(nc) samples. We remove proteins from the ‘cl’ which were also present in ‘nc’ as we cannot comment on enrichment.
Interestingly, the oligodT data seems a lot “cleaner” than trizol dataset. By this I mean, the top domains are most definitely all known RNA-binding domains based on literature looking at RBPs (Lunde 2007, Burd 1994). In the Trizol data we get “Ig-like” domains as one of the top hits which we don’t see at all in the oligodT data.
Between crosslinked and non-crosslinked samples, we still see some overlap in that we are getting RNA-binding domains and proteins in non-crosslinked samples but the number of such proteins in a LOT lower in non-crosslinked than in crosslinked samples.
obj.names = c("oligo.cl.in.nc","oligo.cl","oligo.nc","cl.and.nc.prot")
count = 0
for(y in list(oligo.cl.in.nc,oligo.cl,oligo.nc,cl.and.nc.prot)){
count = count+1
y = queryMany(y,scopes="uniprot",fields=c("ensembl","name","symbol","interpro","go"))
y$domains = sapply(sapply(y$interpro,"[[",3),function(x) paste(x,collapse="; "))
y$num.rbds = rowSums(sapply(rbd.doms, function(x) grepl(x, y$domains)))
y$which.rbd = apply(sapply(rbd.doms, function(x) grepl(x, y$domains)),1, function(z) paste(names(which(z==T)),collapse="; "))
print(table(y$num.rbds))
print(paste("Percentage of proteins with RNA-binding domains in ", obj.names[count]," = ",round(100*sum(table(y$num.rbds)[2:4])/sum(table(y$num.rbds)),2),sep=""))
}
Finished
Pass returnall=TRUE to return lists of duplicate or missing query terms.
0 1 2 3
449 107 99 13
[1] "Percentage of proteins with RNA-binding domains in oligo.cl.in.nc = 32.78"
Finished
Pass returnall=TRUE to return lists of duplicate or missing query terms.
0 1 2 3
338 83 63 10
[1] "Percentage of proteins with RNA-binding domains in oligo.cl = 31.58"
Finished
Pass returnall=TRUE to return lists of duplicate or missing query terms.
0 1 2 3
119 27 37 3
[1] "Percentage of proteins with RNA-binding domains in oligo.nc = 36.02"
Finished
Pass returnall=TRUE to return lists of duplicate or missing query terms.
0 1 2 3
111 24 36 3
[1] "Percentage of proteins with RNA-binding domains in cl.and.nc.prot = 36.21"
#-----------------------------------------------------------------------
# 10 : Looking at the intersect of proteins between oligodT and Trizol
#-----------------------------------------------------------------------
# Using Trizol mapped to hgnc_symbol as oligodT is only in symbols.
library(venn)
Trizol = unique(uv.qm$query)
oligodT = unique(oligo.cl)
v = venn(list(Trizol=Trizol,oligodT=oligodT),intersections=T)
both = attr(v,"intersection")$`Trizol:oligodT` # n = 298
oligo.only = attr(v,"intersection")$`oligodT` # n = 191
trizol.only = attr(v,"intersection")$`Trizol` # n = 1446
# Enrichment for overlaps and setdiffs
m = c("both","oligo-only","trizol-only")
c = 0
for(t in list(both=both,oligo.only=oligo.only,trizol.only=trizol.only)){
c=c+1
t.enrich.go = runGoseq(t,bias.df, bias.df$protbias,geiger.cat.go)
t.enrich.interpro = runGoseq(t,bias.df, bias.df$protbias,geiger.cat)
write.table(t.enrich.go[[2]][,c(1,6:7,4:5,2,8)],paste(outdir,paste(m[c],"-genes-GO-enrichment.txt",sep=""),sep="/"),sep="\t",row.names=F,quote=F)
write.table(t.enrich.interpro[[2]],paste(outdir, paste(m[c],"-genes-Interpro-enrichment.txt",sep=""),sep="/"),sep="\t",row.names=F,quote=F)
}
oo = intersect(oligo.only,geiger.qm$query) # 170/191
tt = intersect(trizol.only,geiger.qm$query) # 1144/1446
bl = intersect(both,geiger.qm$query) # 297/298 mising EIF3C/Q99613
#------------------------------------------------------------------------------------------------------------------------
# Step 09 : Performing enrichment analysis using 'clusterProfiler' and 'goseq' packages
# In the first instance we will use GO terms and KEGG terms for enrichment analysis
#------------------------------------------------------------------------------------------------------------------------
geiger = as.character(unique(fData(agg.u2os)$master_protein))
beck = read.table("Input/Beck2011_n5781.txt")
beck = as.character(beck$V1)
lundberg.ens = read.table("Input/Lundberg2010_n5480.txt")
lundberg = unique(bitr(as.character(lundberg.ens$V1),fromType="ENSEMBL", toType="UNIPROT", OrgDb="org.Hs.eg.db")$UNIPROT)
'select()' returned 1:many mapping between keys and columns
2.81% of input gene IDs are fail to map...
length(lundberg)
[1] 9892
library(venn)
Attaching package: ‘venn’
The following object is masked from ‘package:gplots’:
venn
library(gplots)
library(limma)
venn.u2os = venn(list(Geiger2012=geiger,Beck2011=beck,Lundberg2010=lundberg))

Small exercise on how much Geiger et al, Beck et al, and Lundberg et al., protein sets from Mass Spectrometery experiments overlap. With Lundberg et al., I had to map Ensembl IDs to UNIPROT and then do the comparison which caused a one-to-many mapping. The excess 4941 only found in Lundberg et al is almost exclusively due to the mapping of one Ensembl ID to many UNIPROT IDs. Geiger et al, being the most recent study does claim to have maximal protein mapping for U2OS. Beck et al.,
Should we use just the 4145 that overlaps as our high confident set or focus on Geiger as it the most recent ?
LS0tCnRpdGxlOiAiQW5hbHlzaW5nIHRoZSBmaXJzdCBzZXQgb2YgVE1ULXRhZ2dlZCBkYXRhIGZvciBUcml6b2wtYmFzZWQgVVYgZG9zYWdlIGV4cGVyaW1lbnRzIgpvdXRwdXQ6CiAgcGRmX2RvY3VtZW50OiBkZWZhdWx0CiAgaHRtbF9ub3RlYm9vazogZGVmYXVsdAogIGh0bWxfZG9jdW1lbnQ6IGRlZmF1bHQKLS0tCgpgYGB7ciBnbG9iYWxfb3B0aW9ucywgaW5jbHVkZT1GQUxTRX0Ka25pdHI6Om9wdHNfY2h1bmskc2V0KGZpZy53aWR0aD0xMixmaWcuaGVpZ2h0PTgsd2FybmluZz1GQUxTRSwgbWVzc2FnZT1GQUxTRSkKI3RpZHkub3B0cz1saXN0KHdpZHRoLmN1dG9mZj04MCkKYGBgCgpgYGB7ciBBX1N0YXJ0dXAsIGhpZGU9VCx3YXJuaW5nPUZBTFNFLCBtZXNzYWdlPUZBTFNFfQojLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tCiMgQXV0aG9yIAkgICAgICA6IE1hbmFzYSBSYW1ha3Jpc2huYSwgbXIzMjVAbGUuYWMudWsKIyBEYXRlIHN0YXJ0ZWQgCTogNHRoIEF1Z3VzdCwgMjAxNwojIExhc3QgbW9kaWZpZWQgOiA0dGggQXVndXN0LCAyMDE3CiMgQWltIAkJICAgICAgOiBUbyB0YWtlIGEgbG9vayBhdCBmaXJzdCBTSUxBQyBsYWJlbGxlZCBMT1BJVCBkYXRhIG9uIFRyaXpvbAojIERlcGVuZHMgICAgICAgOiBPbiAnc2lsYWNGdW5jdGlvbnMuUicuIE1ha2Ugc3VyZSB0aGV5IGFyZSBpbiB0aGUgc2FtZSBkaXJlY3RvcnkKIyBOb3RlcyAgICAgICAgIDogV29ya3Mgb24gZGF0YSBmcm9tIFJheW5lcidzIGZpcnN0IGV4cGVyaW1lbnRzCiMtLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0gCgoKIyBJbnZva2luZyBsaWJyYXJpZXMKbGlicmFyeShNU25iYXNlKQpsaWJyYXJ5KGdwbG90cykKbGlicmFyeShyZXNoYXBlMikKbGlicmFyeShWSU0pCmxpYnJhcnkoem9vKSAKbGlicmFyeShzcGF0c3RhdCkgIyAiaW0iIGZ1bmN0aW9uIApsaWJyYXJ5KGdnYmlwbG90KQpsaWJyYXJ5KG9yZy5Icy5lZy5kYikKbGlicmFyeShjbHVzdGVyUHJvZmlsZXIpCmxpYnJhcnkoImJpb21hUnQiKQpsaWJyYXJ5KGdvc2VxKQpsaWJyYXJ5KGxpbW1hKQpsaWJyYXJ5KGdncGxvdDIpCgpsaWJyYXJ5KG91dGxpZXJzKQpsaWJyYXJ5KFJDb2xvckJyZXdlcikKbGlicmFyeShzdHJpbmdyKQoKI1NldHRpbmcgd29ya2luZyBkaXJlY3Rvcmllcwp3ZCA9ICIvVXNlcnMvbWFuYXNhL0RvY3VtZW50cy9Xb3JrL1RUVC9UaHJlZVRzL3Byb3Rlb21pY3Mvb2xpZ29kVC8iCnNldHdkKHdkKQpnZXR3ZCgpCgppbmRpciA9IHBhc3RlKHdkLCJJbnB1dCIsc2VwPSIvIikKb3V0ZGlyID0gcGFzdGUod2QscGFzdGUoU3lzLkRhdGUoKSwiT3V0cHV0IixzZXAgPSAiXyIpLHNlcCA9ICIvIikKCmlmIChleGlzdHMob3V0ZGlyKSl7CiAgcHJpbnQoIk91dGRpciBleGlzdHMiKQp9ZWxzZXsKICBkaXIuY3JlYXRlKG91dGRpcikKfQoKYGBgCk5vdyB0aGF0IHdlIGhhdmUgbG9hZGVkIGFsbCB0aGUgcGFja2FnZXMgd2UgbmVlZCBmb3Igd29ya2luZyB3aXRoIHRoaXMgZGF0YSwgbGV0J3MgbW92ZSBvbiB0byB0aGUgZGF0YS4gCgpgYGB7ciAwMF9SZWFkaW5nRGF0YX0KCiMgLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0KIyBTdGVwIDAwOiBSZWFkIGRhdGEKIyBSZWFkIGluIGFsbCB0aGUgZGF0YSByZXF1aXJlZCBmb3IgYW5hbHlzaXMKIyAtLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLQoKIyBGaWxlIG9mIGNvbnRhbWluYW50cyAtIHByb3RlaW5zIHRvIGV4Y2x1ZGUgZnJvbSBhbmFseXNpcyBhcyBhcmUgdGhpbmdzIGxpa2Uga2VyYXRpbiwgYWxjb2hvbCBkZWh5ZHJvZ2VuYXNlIGV0Yy4uLi4KY29udGFtID0gcmVhZC5kZWxpbSgiSW5wdXQvQ29tbW9uIGNvbnRhbWluYW50X2FsbC5jc3YiLHNlcD0iLCIsaGVhZGVyPVQpCgojIFJlYWQgaW4gdGhlIHNhbXBsZSBmaWxlIHRoYXQgbWF0Y2hlcyBjb2x1bW5zIHRvIHNhbXBsZSBjb250ZW50cwpzYW1wLmRhdCA9IHJlYWQuZGVsaW0oIklucHV0L3NhbXBsZXMudHh0IixzZXA9Ilx0IixoZWFkZXI9VCkKCiMgUmVhZCBpbiB0aGUgZGF0YSBmaWxlcyB0aGF0IGNvbnRhaW4gcGVwdGlkZSBsZXZlbCBvdXRwdXQgZnJvbSBQcm90ZW9tZSBkaXNjb3ZlcmVyLi4uCiMgTm90ZTogVGhlIGNvbHVtbnMgdGhhdCBiZWdpbiB3aXRoICJGb3VuZC5pbi5TYW1wbGUuaW4iIGNvcnJlc3BvbmQgdG8gdmFyaW91cyBzYW1wbGVzIGluIHRoZSBzdHVkeS4KIyBDb2x1bW5zIG9mIGludGVyZXN0IGFyZSAic2VxdWVuY2UiLCAibW9kaWZpY2F0aW9ucyIsIm1hc3Rlci5wcm90ZWluLmFjY2Vzc2lvbnMiLCJhYnVuZGFuY2UiLCJxdWFuLmluZm8iCmRhdGEgPSByZWFkLmRlbGltKCJJbnB1dC9Eb3NhZ2VzXzRzdGVwLVRyaXpvbF9QZXB0aWRlR3JvdXBzLnR4dCIsc2VwPSJcdCIsY29tbWVudC5jaGFyPSIiLGFzLmlzPVQsaGVhZGVyPVQpCgojIFN1YnNldCBkYXRhIHRvIG9ubHkga2VlcCBjb2x1bW5zIG9mIGludGVyZXN0CnByb3QuZGF0YSA9IGRhdGFbLGMoMTo2LDEwOjE0LDE3LDQyOjUxLDYyKV0KCiMgUmVuYW1lIHRtdCB0YWdnZWQgY29sdW1ucyB3aXRoIFVWIGRvc2FnZSBuYW1lcwpmb3IoaSBpbiAxOm5yb3coc2FtcC5kYXQpKXsKICBpZCA9IGdyZXAoc2FtcC5kYXQkVE1UW2ldLGNvbG5hbWVzKHByb3QuZGF0YSkpCiAgY29sbmFtZXMocHJvdC5kYXRhKVtpZF0gPSBwYXN0ZShzYW1wLmRhdCRTYW1wbGVbaV0pCn0KZGltKHByb3QuZGF0YSkKaGVhZChwcm90LmRhdGEpCmBgYApkYXRhIGhhcyAyMyBjb2x1bW5zIGFuZCAxNDQ1NiByb3dzIC0gZWFjaCByb3cgYmVsb25naW5nIHRvIGEgcGVwdGlkZSBhYnVuZGFuY2UgdmFsdWUgYWNyb3NzIGFsbCAxMCBzYW1wbGVzLiBXZSBub3cgZ28gdGhyb3VnaCBhIHNlcmllcyBvZiBmaWx0ZXJpbmcgc3RlcHMgdG8gb2J0YWluIGEgZGF0YXNldCB3ZSBjYW4gdXNlIGZvciBkb3duc3RyZWFtIGFuYWx5c2VzLiAKCgpgYGB7ciAwMV9GaWx0ZXJpbmdfMX0KCiMgLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tCiMgU3RlcCAwMSA6IEZpbHRlciAKIyBXZSBwZXJmb3JtIDMgbGF5ZXJzIG9mIGZpbHRlcmluZyAtIHVuaXF1ZSBwcm90ZWlucywgY29udGFtaW5hbnRzLG1pc3NpbmcgdmFsdWVzCiMgLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tCgojIFN0ZXAgMWEgOiBGaWx0ZXIgb25seSBmb3IgdGhvc2UgcGVwdGlkZXMgdGhhdCBoYXZlIGEgdW5pcXVlIG1hc3RlciBwcm90ZWluLiBEb25lIHVzaW5nIGNvbHVtbiAicXVhbi5pbmZvIiBhbmQgdGl0bGVkICdVbmlxdWUnCnBlcHRpZGUuc3RhdHMgPSB0YWJsZShwcm90LmRhdGEkUXVhbi5JbmZvKQpwZXB0aWRlLnN0YXRzCgpmaWx0LjFhID0gcHJvdC5kYXRhW3doaWNoKHByb3QuZGF0YSRRdWFuLkluZm8gPT0gIlVuaXF1ZSIpLF0KZGltKGZpbHQuMWEpICMxMjMwMSBhcmUgdW5pcXVlIHBlcHRpZGVzLCA3MTUgYXJlIG5vbi11bmlxdWUgYW5kIDE0NDAgYXJlIG1pc3NpbmcgdmFsdWVzIAoKIyBTdGVwIDFiIDogRmlsdGVyIG91dCB0aG9zZSBwcm90ZWlucyB0aGF0IGFyZSBjb250YW1pbmFudHMgZnJvbSB0aGUgY29udGFtaW5hbnRzIGxpc3QgYW5kIGFubm90YXRlIG1pc3NpbmcgdmFsdWVzCmZpbHQuMWIgPSBmaWx0LjFhWy13aGljaChmaWx0LjFhJE1hc3Rlci5Qcm90ZWluLkFjY2Vzc2lvbnMgJWluJSBjb250YW0kUHJvdGVpbi5Hcm91cC5BY2Nlc3Npb25zKSxdCm51bS5jb250YW1zID0gbGVuZ3RoKHdoaWNoKGZpbHQuMWEkTWFzdGVyLlByb3RlaW4uQWNjZXNzaW9ucyAlaW4lIGNvbnRhbSRQcm90ZWluLkdyb3VwLkFjY2Vzc2lvbnMpKQoKZGltKGZpbHQuMWEpICMgMTIzMDEgaW4gdG90YWwKZGltKGZpbHQuMWIpICMgMTIwNzcgZmlsdGVyZWQgcHJvdGVpbnMKcHJpbnQobnVtLmNvbnRhbXMpICMgMjI0IGNvbnRhbWluYW50IHByb3RlaW5zCgojIEFkZGluZyBleHRyYSBpbmZvcm1hdGlvbiBhYm91dCByb3dzIHdpdGggbWlzc2luZyB2YWx1ZXMKZmlsdC4xYiRjb3VudC5taXNzaW5nID0gcm93U3Vtcyhpcy5uYShmaWx0LjFiWyxjKDEzOjIyKV0pKQoKZmlsdC4xYiRNaXNzaW5nID0gRkFMU0UKZmlsdC4xYiRNaXNzaW5nW3doaWNoKGZpbHQuMWIkY291bnQubWlzc2luZyA+IDApXSA9IFRSVUUKCmhlYWQoZmlsdC4xYikKCmBgYApXZSBoYXZlIGEgY29sdW1uIGNhbGxlZCAiTWlzc2luZyIgdG8gaWRlbnRpZnkgd2hpY2ggcGVwdGlkZXMgaGF2ZSBvbmUgb3IgbW9yZSBtaXNzaW5nIHZhbHVlcyBhY3Jvc3MgdGhlIDEwIHNhbXBsZXMuICJjb3VudC5taXNzaW5nIiB0ZWxscyB1cyBob3cgbWFueSBtaXNzaW5nIHZhbHVlcyB0aGVyZSBhcmUgZm9yIHRoYXQgcGVwdGlkZS4KCgpgYGB7ciAwMmFfQ3JlYXRpbmctYW4tTVNuU2V0fQoKIyAtLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0KIyBTdGVwIDAyYSA6IENyZWF0aW5nIGFuIE1TblNldCB3aGljaCBpcyBuZWVkZWQgZm9yIHVzaW5nIHRoZSBNU25iYXNlIGJhY2thZ2UKIyAtLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0KCiMgVGhlIHJvd25hbWVzIG9mIHNhbXAuZGF0IGhhdmUgdG8gYmUgdGhlIHNhbWUgYXMgY29sdW1uIG5hbWVzIGluIHRoZSBleHByZXNzaW9uIGRhdGEgbWF0cml4CnJvd25hbWVzKHNhbXAuZGF0KSA9IHNhbXAuZGF0JFNhbXBsZQoKIyBDcmVhdGUgYW4gTVNuU2V0IG9iamVjdApyZXMgPC0gTVNuU2V0KGV4cHJzID0gYXMubWF0cml4KGZpbHQuMWJbLGMoMTM6MjIpXSksZkRhdGE9ZmlsdC4xYlssYygxMCwxOjksMTIsMjM6MjUpXSxwRGF0YSA9IHNhbXAuZGF0WyxjKDIsMSldKQpyZXMgPC0gcmVzW3Jvd1N1bXMoaXMubmEoZXhwcnMocmVzKSkpIT0xMCxdICMgZXhjbHVkZSAgcGVwdGlkZXMgd2l0aG91dCBhbnkgcXVhbnRpZmljYXRpb24KcHJpbnQocmVzKQoKIyBIb3cgbWFueSBtaXNzaW5nIHZhbHVlcyBwZXIgcGVwdGlkZQp0YWJsZShmRGF0YShyZXMpJGNvdW50Lm1pc3NpbmcpCmNvbFN1bXMoaXMubmEoZXhwcnMocmVzKSkpCnRhYmxlKHJvd1N1bXMoaXMubmEoZXhwcnMocmVzKSkpKQoKIyBDaGVja2luZyBtaXNzaW5nIHZhbHVlcwp0YWJsZShpcy5uYShyZXMpKQpgYGAKVGhlIE1TblNldCBvYmplY3QgaGFzIGJlZW4gY3JlYXRlZCB0byBpbmNsdWRlIHByb3RlaW4gYWJ1bmRhbmNlIHZhbHVlcywgc29tZSBtZXRhZGF0YSBhbmQgc2FtcGxlIGluZm9ybWF0aW9uIChVViBkb3NhZ2UgaW4gdGhpcyBjYXNlKQoKYGBge3IgMDJiX0ltcHV0aW5nLW1pc3NpbmctdmFsdWVzfQoKIyAtLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0KIyBTdGVwIDAyYiA6IEltcHV0aW5nIG1pc3NpbmcgdmFsdWVzIGluIHRoZSBwcm90ZWluIGV4cHJlc3Npb24gZGF0YSB1c2luZyAnaW1wdXRlJwojIC0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLQoKIyBTdWJzZXR0aW5nIG9ubHkgdGhvc2UgcGVwdGlkZXMgd2l0aCBvbmUgb3IgbW9yZSBtaXNzaW5nIHZhbHVlcwojIFJlcGxhY2luZyBtaXNzaW5nIHZhbHVlcyB3aXRoIDAgYW5kIG5vbi1taXNzaW5nIHdpdGggMQojIERpc3BsYXlpbmcgbWlzc2luZyB2YWx1ZXMKCm1pc3MubWFueSA9IHJlc1tyb3dTdW1zKGlzLm5hKGV4cHJzKHJlcykpKT49MSxdCmV4cHJzKG1pc3MubWFueSlbZXhwcnMobWlzcy5tYW55KSAhPSAwXSA9IDEKZXhwcnMobWlzcy5tYW55KVtpcy5uYShleHBycyhtaXNzLm1hbnkpKV0gPSAwCgpoZWF0bWFwLjIoZXhwcnMobWlzcy5tYW55KSwgY29sID0gYygibGlnaHRncmF5IiwgImJsYWNrIiksCiAgICAgICAgICAgIHNjYWxlID0gIm5vbmUiLCBkZW5kcm9ncmFtID0gIm5vbmUiLAogICAgICAgICAgICB0cmFjZSA9ICJub25lIiwga2V5c2l6ZSA9IDAuNSwga2V5ID0gRkFMU0UsQ29sdj1GLAogICAgICAgICAgICBDb2xTaWRlQ29sb3JzID0gcmVwKGMoInN0ZWVsYmx1ZSIsICJkYXJrb2xpdmVncmVlbiIsIm1hZ2VudGEiLCJibGFjayIpLCB0aW1lcyA9IGMoMywzLDMsMSkpKQoKIyBJbXB1dGUgbWlzc2luZyB2YWx1ZXMgCmltcHV0ZS5yZXMgPC0gaW1wdXRlKHJlcyxtZXRob2QgPSAia25uIikKcERhdGEoaW1wdXRlLnJlcykkU2FtcGxlID0gZ3N1YignXFxzKycsJycscERhdGEoaW1wdXRlLnJlcykkU2FtcGxlKQoKIyBQbG90IGltcHV0ZWQgdmFsdWVzIApyZXMubWlzcyA9IG1lbHQoZXhwcnMocmVzKSkKY29sbmFtZXMocmVzLm1pc3MpID0gYygiUm93IiwiRG9zYWdlIiwiQWJ1bmRhbmNlX2ltcCIpCnJlcy5uby5taXNzID0gbWVsdChleHBycyhpbXB1dGUucmVzKSkKY29sbmFtZXMocmVzLm5vLm1pc3MpID0gYygiUm93IiwiRG9zYWdlIiwiQWJ1bmRhbmNlIikKCmltcC52YWxzID0gcmVzLm5vLm1pc3Nbd2hpY2goaXMubmEocmVzLm1pc3MkQWJ1bmRhbmNlX2ltcCkpLF0KCiMgU29tZSBib3hwbG90cyBvZiB0aGUgZGF0YQojLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tCgojIEFsbCBkYXRhIGluY2x1ZGluZyBtaXNzaW5nIHZhbHVlcwpib3hwbG90KGxvZzIocmVzLm1pc3MkQWJ1bmRhbmNlKX5hcy5mYWN0b3IocmVzLm1pc3MkRG9zYWdlKSxsYXM9Mixjb2w9cmVwKGMoInR1cnF1b2lzZSIsICJzYWxtb24iLCJwYWxlZ3JlZW4iLCJwbHVtMSIpLHRpbWVzPWMoMywzLDMsMSkpLG1haW49IkFsbCBkYXRhIGluY2x1ZGluZyBtaXNzaW5nIHZhbHVlcyIpCgojIEltcHV0ZWQgdmFsdWVzL21pc3NpbmcgdmFsdWVzIG9ubHkKYi5pbXAgPSBib3hwbG90KGxvZzIoaW1wLnZhbHMkQWJ1bmRhbmNlKX5pbXAudmFscyREb3NhZ2UsbGFzPTIsY29sPXJlcChjKCJ0dXJxdW9pc2UiLCAic2FsbW9uIiwicGFsZWdyZWVuIiwicGx1bTEiKSx0aW1lcz1jKDMsMywzLDEpKSxtYWluPSJJbXB1dGVkIHZhbHVlcyBvbmx5IikKYm94cGxvdChsb2cyKGltcC52YWxzJEFidW5kYW5jZSl+aW1wLnZhbHMkRG9zYWdlLGxhcz0yLG5hbWVzID0gcGFzdGUoYi5pbXAkbmFtZXMsIiAobj0iLCBiLmltcCRuLCAiKSIsc2VwPSIiKSxjb2w9cmVwKGMoInR1cnF1b2lzZSIsICJzYWxtb24iLCJwYWxlZ3JlZW4iLCJwbHVtMSIpLHRpbWVzPWMoMywzLDMsMSkpLG1haW49IkltcHV0ZWQgdmFsdWVzIG9ubHkiLCxjZXguYXhpcyA9IDAuNikKCiMgQWxsIGRhdGEgaWNsdWRpbmcgbmV3bHkgaW1wdXRlZCB2YWx1ZXMKYm94cGxvdChsb2cyKHJlcy5uby5taXNzJEFidW5kYW5jZSl+YXMuZmFjdG9yKHJlcy5uby5taXNzJERvc2FnZSksbGFzPTIsY29sPXJlcChjKCJ0dXJxdW9pc2UiLCAic2FsbW9uIiwicGFsZWdyZWVuIiwicGx1bTEiKSx0aW1lcz1jKDMsMywzLDEpKSxtYWluPSJBbGwgZGF0YSB3aXRoIGltcHV0ZWQgdmFsdWVzIikKCiMgQWRkaXRpb25hbCBwbG90cyBzaG93aW5nIGZyYWN0aW9uIG9mIG1pc3NpbmcgdmFsdWVzCiMgTm90IG11Y2ggdXNlIGFzIGluIG91ciBjYXNlLCBpdCBpcyB2ZXJ5IHNtYWxsLiAKCnZpbS5kYXQgPSBkYXRhLmZyYW1lKGNiaW5kKEFidW5kYW5jZV9pbXA9cmVzLm1pc3MkQWJ1bmRhbmNlX2ltcCxBYnVuZGFuY2U9cmVzLm5vLm1pc3MkQWJ1bmRhbmNlKSxzdHJpbmdzQXNGYWN0b3JzID0gRikKCmhlYWQodmltLmRhdCkKdmltLmRhdCR3aGljaC5taXNzID0gRkFMU0UKdmltLmRhdCR3aGljaC5taXNzW3doaWNoKGlzLm5hKHZpbS5kYXQkQWJ1bmRhbmNlX2ltcCkpXSA9IFRSVUUKdGFibGUodmltLmRhdCR3aGljaC5taXNzKQoKaGlzdE1pc3ModmltLmRhdCwgb25seS5taXNzID0gRikKcGJveCh2aW0uZGF0LCB5bGltPWMoMCw1MDApLHNlbGVjdGlvbj0ibm9uZSIpCm1hdHJpeHBsb3QodmltLmRhdCkKCmBgYApXZSBoYXZlIGEgc21hbGwgbnVtYmVyIG9mIG1pc3NpbmcgdmFsdWVzIC0gNzkzIHBlcHRpZGVzIGhhdmUgb25lIG9yIG1vcmUgbWlzc2luZyB2YWx1ZXMgb3V0IG9mIDEyMjQ0IHBlcHRpZGVzIGluIHRvdGFsLiA1NTEvNzkzIGFyZSBtaXNzaW5nIGluIDEgc2FtcGxlIG9ubHkgYW5kIDM4Lzc5MyBpbiAyIHNhbXBsZXMuIE9ubHkgMiBwZXB0aWRlcyBhcmUgbWlzc2luZyBpbiA5LzEwIHNhbXBsZXMuCgpgYGB7ciAwM19Ob3JtYWxpc2F0aW9ufQoKIyAtLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0KIyBTdGVwIDAzIDogTm9ybWFsaXNpbmcgaW1wdXRlZCBkYXRhIHVzaW5nIHZhcmlvdXMgbWV0aG9kcyB0byBkZXRlcm1pbmUgaWRlYWwgb25lCiMgLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tCgpxbnQubWF4IDwtIG5vcm1hbGlzZShpbXB1dGUucmVzLCAibWF4IikKcW50LnN1bSA8LSBub3JtYWxpc2UoaW1wdXRlLnJlcywgInN1bSIpCnFudC5xdWFudCA8LSBub3JtYWxpc2UoaW1wdXRlLnJlcywgInF1YW50aWxlcyIpCnFudC5xcm9iIDwtIG5vcm1hbGlzZShpbXB1dGUucmVzLCAicXVhbnRpbGVzLnJvYnVzdCIpCnFudC52c24gPC0gbm9ybWFsaXNlKGltcHV0ZS5yZXMsICJ2c24iKQoKIyMgLS0tLSBwbG90dGluZyBmdW5jdGlvbi0tLS0tLS0tLQoucGxvdCA8LSBmdW5jdGlvbih4LHR0bD1OVUxMKSB7CiAgYm94cGxvdChleHBycyh4KSwKICAgICAgICAgIG1haW49aWZlbHNlKGlzLm51bGwodHRsKSxwcm9jZXNzaW5nRGF0YSh4KUBwcm9jZXNzaW5nWzJdLHR0bCksCiAgICAgICAgICBjZXgubWFpbj0xLjUsCiAgICAgICAgICBjZXgubGFiPS41LAogICAgICAgICAgY2V4LmF4aXM9MC44LAogICAgICAgICAgY2V4PS44LAogICAgICAgICAgbGFzPTIpCiAgZ3JpZCgpCn0KCiMgVXNpbmcgdGhlIHBsb3R0aW5nIGZ1bmN0aW9uIHRvIHBsb3QgYm94cGxvdHMgZm9yIGFsbCBkaWZmIHR5cGVzIG9mIG5vcm1hbGlzYXRpb24gbWV0aG9kcwpvbGRtYXIgPC0gcGFyKCkkbWFyCnBhcihtZnJvdz1jKDMsMiksbWFyPWMoMi45LDIuOSwyLjksMSkpCi5wbG90KGltcHV0ZS5yZXMsIHR0bCA9ICJOb24tbm9ybWFsaXNlZCBkYXRhIikKLnBsb3QocW50Lm1heCwgdHRsID0gIk1heGltdW0iKQoucGxvdChxbnQuc3VtLCB0dGwgPSAiU3VtIikKLnBsb3QocW50LnF1YW50LCB0dGwgPSAiUXVhbnRpbGUiKQoucGxvdChxbnQucXJvYiwgdHRsID0gIlJvYnVzdCBxdWFudGlsZSIpCi5wbG90KHFudC52c24sIHR0bCA9ICJ2c24iKQoKIyBDaGVja2luZyB0aGUgZWZmZWN0cyBvZiBub3JtYWxpc2F0aW9uIG9uIGNvdmFyaWFuY2UKc2QxIDwtIGFwcGx5KGxvZzIoZXhwcnMoaW1wdXRlLnJlcykpKzEwLDEsc2QpCm1uMSA8LSBhcHBseShsb2cyKGV4cHJzKGltcHV0ZS5yZXMpKSsxMCwxLG1lYW4pCmN2MSA8LSBzZDEvbW4xCnNkMiA8LSBhcHBseShleHBycyhxbnQudnNuKSsxMCwxLHNkKQptbjIgPC0gYXBwbHkoZXhwcnMocW50LnZzbikrMTAsMSxtZWFuKQpjdjIgPC0gc2QyL21uMgpkZnIgPC0gcmJpbmQoZGF0YS5mcmFtZShyYW5rPW9yZGVyKG1uMSksY3Y9Y3YxLG5vcm09InJhdyIpLAogICAgICAgICAgICAgZGF0YS5mcmFtZShyYW5rPW9yZGVyKG1uMiksY3Y9Y3YyLG5vcm09InZzbiIpKQoKcm1lZDEgPC0gcm9sbGFwcGx5KGN2MSw3LGZ1bmN0aW9uKHgpIG1lZGlhbih4LG5hLnJtPVRSVUUpKQpybWVkMiA8LSByb2xsYXBwbHkoY3YyLDcsZnVuY3Rpb24oeCkgbWVkaWFuKHgsbmEucm09VFJVRSkpCmRmcjIgPC0gcmJpbmQoZGF0YS5mcmFtZSh4PXNlcSgwLDMwLGJ5PTMwL2xlbmd0aChybWVkMSkpWy0xXSx5PXJtZWQxLG5vcm09InJhdyIpLGRhdGEuZnJhbWUoeD1zZXEoMCwzMCxieT0zMC9sZW5ndGgocm1lZDIpKVstMV0seT1ybWVkMixub3JtPSJ2c24iKSkKCnAgPC0gZ2dwbG90KCkrZ2VvbV9saW5lKGRhdGE9ZGZyMixhZXMoeD14LHk9eSxjb2w9bm9ybSkpICsgdGhlbWVfZ3JheSg3KQpwbG90KHApCmBgYApXaWxsIGtlZXAgdGhlIGRhdGEgZnJvbSB0aGUgdnNuIG5vcm1hbGlzYXRpb24gZm9yIGRvd25zdHJlYW0gYW5hbHlzZXMgYXMgaXQgbm9ybWFsaXNlcyB0aGUgZGF0YSBiZXR0ZXIgdGhhbiBvdGhlciBtZXRob2RzIHVzZWQgaW4gdGhpcyBjb21wYXJpc29uIHN1Y2ggYXMgInN1bSIsICJtYXgiLCAicXVhbnRpbGUiLiAKCkZvciBhbGwgZnVydGhlciBzdGVwcywgd2Ugd2lsbCB1c2UgdGhlIG9iamVjdCAicW50LnZzbiIKCmBgYHtyIDA0YV9BZ2dyZWdhdGUtdG8tcHJvdGVpbnN9CgojIC0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLQojIFN0ZXAgMDRhIDogQWdncmVnYXRlIHBlcHRpZGUgZGF0YSB0byBwcm90ZWluIGV4cHJlc3Npb24gdmFsdWVzCiMgVGhlcmUgaXMgYW4gaW4tYnVpbHQgZnVuY3Rpb24gY2FsbGVkICdjb21iaW5lRmVhdHVyZXMnIHRvIGRvIHRoaSB3aXRoaW4gTVNuQmFzZQojIC0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLQoKcHJvdG5hbWVzIDwtIGZEYXRhKHFudC52c24pJE1hc3Rlci5Qcm90ZWluLkFjY2Vzc2lvbnMKI3RhYmxlKHByb3RuYW1lcykKbGVuZ3RoKHVuaXF1ZShwcm90bmFtZXMpKSAjIDE3NDQgcHJvdGVpbnMgcHJlc2VudCBpbiBhbGwgMTAgc2FtcGxlcwoKIyBBZ2dyZWdhdGluZyBwZXB0aWRlIGFidW5kYW5jZSB2YWx1ZXMgaW50byBwcm90ZWluIGFidW5kYW5jZSB2YWx1ZXMKcW50LnByb3QgPC0gY29tYmluZUZlYXR1cmVzKHFudC52c24sIGdyb3VwQnkgPSBwcm90bmFtZXMsIGZ1biA9ICJtZWRpYW4iKQpkaW0ocW50LnByb3QpCgpoZWFkKGV4cHJzKHFudC5wcm90KSkKaGVhZChleHBycyhxbnQudnNuKSkKCiMgQmFzaWMgcGxvdHMgb2YgcHJvdGVpbiBkYXRhIGFjcm9zcyBzYW1wbGVzCi5wbG90KHFudC5wcm90LHR0bD0iQWdncmVnYXRlZC1wcm90ZWlucyIpCnBsb3QoaGNsdXN0KGRpc3QoZXhwcnModChxbnQucHJvdCkpKSkpCgojIExvb2tpbmcgYXQgc2FtcGxlIGNvcnJlbGF0aW9ucwpjb3IucHJvdCA9IGNvcihleHBycyhxbnQucHJvdCkpCmhlYXRtYXAoY29yLnByb3QsY2V4Lm1haW4gPSAwLjgpCgpkaXNzaW1pbGFyaXR5IDwtIDEgLSBjb3IucHJvdApkaXN0YW5jZSA8LSBhcy5kaXN0KGRpc3NpbWlsYXJpdHkpCnBsb3QoaGNsdXN0KGRpc3RhbmNlKSkKCnBhaXJzKHggPSBleHBycyhxbnQucHJvdCksIHVwcGVyLnBhbmVsPU5VTEwsIHBjaD0yMCkKCiMgVXNpbmcgdGhlICJkdXBsaWNhdGVjb3JyZWxhdGlvbiIgZnVuY3Rpb24gaW4gbGltbWEgdG8gdGVzdCBjb3JyZWxhdGlvbiBiZXR3ZWVuIHRlY2huaWNhbCByZXBsaWNhdGVzCgojIElmIFJheW5lcidzIG9yZGVyaW5nIGlzIGNvcnJlY3QsIHRoZW4gdGhlIHNhbXBsZXMgYXJlCmJpb2xyZXAucnEgPC0gYygxLDEsMSwyLDIsMiwzLDMsMyw0KQoKIyBJZiB0aGVyZSBpcyBhIHN3YXAgYmV0d2VlbiBQb29sLjEgYW5kIDI3NW1qLnJlcDEsIHRoZW4gaXQgc2hvdWxkIGJlCmJpb2xyZXAuc3dhcCA8LSBjKDEsMSwxLDQsMiwyLDMsMywzLDIpCgojIElmIGl0IGlzIGluZGVlZCBhIHN3YXAsIHRoZW4gdGhlIGNvcnJlbGF0aW9uIHNob3VsZCBpbmNyZWFzZSB3aGVuIGNvcnJlY3RlZC4gSXQgZG9lcyEKcnEuY29yID0gZHVwbGljYXRlQ29ycmVsYXRpb24ocW50LnByb3QsbmR1cHM9MSxibG9jaz1iaW9scmVwLnJxKSRjb25zZW5zdXMuY29ycmVsYXRpb24gIyAwLjIwMwpzd2FwLmNvciA9IGR1cGxpY2F0ZUNvcnJlbGF0aW9uKHFudC5wcm90LG5kdXBzPTEsYmxvY2s9YmlvbHJlcC5zd2FwKSRjb25zZW5zdXMuY29ycmVsYXRpb24gIyAwLjU2NAoKcnEuY29yCnN3YXAuY29yCgpjb3IucHJvdAoKI2R1cGxpY2F0ZUNvcnJlbGF0aW9uKHFudC5wcm90WywxOjldLG5kdXBzPTEsYmxvY2s9YygxLDEsMSwyLDIsMiwzLDMsMykpJGNvbnNlbnN1cy5jb3JyZWxhdGlvbiAjIDAuMjMKI2R1cGxpY2F0ZUNvcnJlbGF0aW9uKHFudC5wcm90WyxjKDE6Myw1OjEwKV0sbmR1cHM9MSxibG9jaz1jKDEsMSwxLDIsMiwzLDMsMywyKSkkY29uc2Vuc3VzLmNvcnJlbGF0aW9uICMgMC4zNwoKYGBgCldlIGhhdmUgbWVyZ2VkIHRoZSBwZXB0aWRlcyBpbnRvIHByb3RlaW5zIGFuZCBhcmUgbG9va2luZyBoZXJlIGF0IHRoZSBjb3JyZWxhdGlvbnMgd2l0aGluIHJlcGxpY2F0ZXMgb2YgVVYgZG9zYWdlLiBSZXBsaWNhdGUgMyBvZiAxNTBtSiBkb3NhZ2UgaXMgYSBiaXQgb2ZmIGZyb20gdGhlIG90aGVyIHR3byB3aGlsZSByZXBsaWNhdGUgMSBvZiAyNzVtSiBzaXRzIGJ5IGl0c2VsZiBhbGxvd2luZyB0aGUgIlBvb2wiIHNhbXBsZSB0byBjbHVzdGVyIHdpdGggdGhlIG90aGVyIDI3NW1KIHNhbXBsZXMuIAoKSSB0aG91Z2h0IHRoYXQgMjc1bUouMyBhbmQgUG9vbCBtaWdodCBoYXZlIGJlZW4gc3dhcHBlZC4gU28gdGhlIHZhcmlvdXMgcGxvdHMgd2VyZSBkb25lIHRvIHByb3ZlIHdoZXRoZXIgb3Igbm90IHRoaXMgd2FzIHRydWUuIFRoZSAiZHVwbGljYXRlQ29ycmVsYXRpb24iIGZ1bmN0aW9uIHlpZWxkcyBhIG11Y2ggaGlnaGVyIGNvcnJlbGF0aW9uIHdoZW4gd2UgYXNzdW1lIHRoYXQgMjc1bUouMyBpcyBzd2FwcGVkIHdpdGggUG9vbC4xIHRoYW4gaWYgd2UgZGlkbid0LiAKCkxvb2tpbmcgYXQgdGggY29ycmVsYXRpb24gdmFsdWVzLCAyNzUubUogdnMgUG9vbCBpcyBtb3JlIGNvcnJlbGF0ZWQgdGhhbiAxNTBtSiB2cyBQb29sIGFuZCA0MDBtSiB2cyBQb29sLiBUaGlzIGNvdWxkIGJlIGJlY3Vhc2UgdGhlcmUgd2FzIG1vcmUgbWF0ZXJpYWwgZnJvbSBzYW1wbGUgMjc1bUogZ29pbmcgaW50byB0aGUgcG9vbC4gUmF5bmVyIHVzZWQgdGhlIHNhbWUgdm9sdW1lIG9mIGVhY2ggb2YgdGhlIDkgc2FtcGxlcyByYXRoZXIgdGhhbiBzYW1lIGFtb3VudCBpbiB0aGUgcG9vbC4gSGVuY2UgdGhlIGhpZ2hlciBjb3JyZWxhdGlvbiAod2VsbCBhdCBsZWFzdCBpdCBpcyBteSBiZXN0IGd1ZXNzKS4gCgpgYGB7ciAwNGJfUGxvdHRpbmctUENBcyB9CgojIC0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLQojIFN0ZXAgMDRhIDogUGxvdHRpbmcgUENBcyAKIyBUaGlzIGlzIHRvIGxvb2sgYXQgdmFyaWFiaWxpdHkgYWNyb3NzIHNhbXBsZXMgYW5kIHdpdGhpbiByZXBsaWNhdGVzCiMgLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tCgojIEFjcm9zcyBhbGwgMTAgc2FtcGxlcwpwcm90LnBjYSA9IHByY29tcCh0KGV4cHJzKHFudC5wcm90KSksc2NhbGU9VCkKaiA8LSBnZ2JpcGxvdChwcm90LnBjYSwgdmFyLmF4ZXM9RiwgZ3JvdXBzID0gZmFjdG9yKGMoMSwxLDEsMiwyLDIsMywzLDMsNCkpLCBjaXJjbGUgPSBULG9icy5zY2FsZT0xLGxhYmVscz1yb3duYW1lcyhwcm90LnBjYSR4KSkKcHJpbnQoaikKCiMgRXhjbHVkaW5nIHRoZSBQb29sLjEgc2FtcGxlCnByb3QucGNhLnJxID0gcHJjb21wKHQoZXhwcnMocW50LnByb3RbLGMoMTo5KV0pKSxzY2FsZT1UKQpnIDwtIGdnYmlwbG90KHByb3QucGNhLnJxLCB2YXIuYXhlcz1GLCBncm91cHMgPSBmYWN0b3IoYygxLDEsMSwyLDIsMiwzLDMsMykpLCBjaXJjbGUgPSBULG9icy5zY2FsZT0xLGxhYmVscz1yb3duYW1lcyhwcm90LnBjYS5ycSR4KSkKZyA8LSBnK2xhYnMoY29sb3VyID0gIlVWIERvc2FnZSIpCnByaW50KGcpCgojIEFzc3VtaW5nIFBvb2wuMSBpcyBhY3R1YWxseSAyNzVtSi5yZXAxCnByb3QucGNhLnN3YXAgPSBwcmNvbXAodChleHBycyhxbnQucHJvdFssYygxOjMsNToxMCldKSksc2NhbGU9VCkKaCA8LSBnZ2JpcGxvdChwcm90LnBjYS5zd2FwLCB2YXIuYXhlcz1GLCBncm91cHMgPSBmYWN0b3IoYygxLDEsMSwyLDIsMywzLDMsMikpLCBjaXJjbGUgPSBULG9icy5zY2FsZT0xLGxhYmVscz1yb3duYW1lcyhwcm90LnBjYS5zd2FwJHgpKQpwcmludChoKQoKIyBSZW1vdmluZyBib3RoIHByb2JsZW0gc2FtcGxlcwpwcm90LnBjYS5uby5kdWQgPSBwcmNvbXAodChleHBycyhxbnQucHJvdFssYygxOjIsNToxMCldKSksc2NhbGU9VCkKayA8LSBnZ2JpcGxvdChwcm90LnBjYS5uby5kdWQsIHZhci5heGVzPUYsIGdyb3VwcyA9IGZhY3RvcihjKDEsMSwyLDIsMywzLDMsNCkpLCBjaXJjbGUgPSBULG9icy5zY2FsZT0xLGxhYmVscz1yb3duYW1lcyhwcm90LnBjYS5uby5kdWQkeCkpCnByaW50KGspCgojIFRvIGZpbmlzaCBvZmYsIHNvbWUgYm94cGxvdHMuLi4uCnBEYXRhKHFudC5wcm90KSA9IHBEYXRhKGltcHV0ZS5yZXMpCgojIEFyZSB0aGUgdmFsdWVzIHZhc3RseSBkaWZmZXJlbnQKYm94cGxvdChleHBycyhxbnQucHJvdCksbGFzPTIpCgpgYGAKSSd2ZSBiZWVuIGxvb2tpbmcgdG8gZXhwbGFpbiB3aGF0IEkgc2VlLiBJdCBzZWVtcyBsaWtlIHRoZSBtb3N0IG9idmlvdXMgYW5zd2VyIHdvdWxkIGJlIHRoYXQgUG9vbC4xIGFuZCAyNzVtSi4xIGFyZSBzd2FwcGVkLiBIb3dldmVyLCB1cG9uIHNwZWFraW5nIHdpdGggUmF5bmVyLCB0aGlzIGRpZCBub3QgaGFwcGVuLiBIZSBzZXRzIHVwIHRoZSB0dWJlcyBpbiBhIHJvdyBhbmQgYWRkcyB0aGUgbGFiZWxzIGluIG9yZGVyIHNvIHRoZSBsYXN0IHNhbXBsZSB3YXMgdGhlIHBvb2wgYW5kIHRoZSA0dGggd2FzIDI3NW1KLjEuIFdoYXQgUmF5bmVyIGRpZCBzYXkgd2FzIHRoYXQgdGhlIHR1YmUgd2l0aCAxNTBtSi4zIHdhcyBkcm9wcGVkIGFuZCBzYW1wbGUgaGFkIGEgbGl0dGxlIHNoYWtlL3R1bWJsZSB3aGljaCBtaWdodCBoYXZlIGFmZmVjdGVkIGl0cyBxdWFsaXR5LiBJIGRvbid0IGtub3cgYXQgd2hpY2ggc3RhZ2Ugb2YgdGhlIHByb3RvY29sIHRoaXMgaGFwcGVuZWQgYnV0IGl0IGRpZC4gVGhpcyB3b3VsZCBleHBsYWluIHRoZSBzZXBhcmF0aW9uIG9mIDE1MG1KLjMgZnJvbSB0aGUgb3RoZXIgdHdvIDE1MG1Kcy4gSG93ZXZlciwgdGhlIDE1MG1KIHRyaXBsaWNhdGUgc3RpbGwgY2x1c3RlciB0b2dldGhlciBvbiB0aGUgaGNsdXN0IHBsb3RzLiBXaXRoIHNhbXBsZSAyNzVtSi4xLCBSYXluZXIgcmVtZW1iZXJzIGl0IGJlaW5nIG9kZCBidXQgd2UgZG9uJ3Qga25vdyB3aHkgb3IgaG93LiAKCkFuIGFsdGVybmF0ZSBleHBsYW5hdGlvbiBpcyB0aGF0IHNhbXBsZSAyNzVtSi4xIGRpZCBub3QgZWx1dGUoPykgb3V0IHByb3Blcmx5IHdoaWxlIGl0IHdhcyBiZWluZyBwcmVwYXJlZCBidXQgaXQgZGlkIGJ5IHRoZSB0aW1lIHRoZSBwb29sIHdhcyBtYWRlLiBUaGUgcG9vbCB3YXMgbWFkZSBvZiBlcXVhbCB2b2x1bWVzIG9mIGFsbCA5IHNhbXBsZXMgcmF0aGVyIHRoYW4gZXF1YWwgcHJvdGVpbiBhbW91bnRzIHNvIHRoZXJlIHdpbGwgYmUgYSBoaWdoZXIgcmVwcmVzZW50YXRpb24gb2YgbW9yZSBhYnVuZGFudCBzYW1wbGVzIGFuZCBsb3dlciBvZiBsZXNzIGFidW5kYW50IHNhbXBsZXMuIAoKSGF2ZSBnb25lIGJhY2sgdG8gbG9vayBhdCB0aGUgImNvci5wcm90IiBtYXRyaXggYW5kIGFzIFRvbSBzdXNwZWN0ZWQsIHRoZSBQb29sLjEgY29ycmVsYXRlcyBzbGlnaHRseSBiZXR0ZXIgd2l0aCBub24tZG9kZ2V5IDI3NW1KIChjb3JyID0gMC45NCkgdGhhbiB3aXRoIDQwMG1KICgwLjg4KSBhbmQgbm9uLWRvZGdleSAxNTBtSiAoMC44NykgYnV0IG5vdCBzaWduaWZpY2FudGx5IG1vcmUsIGhlbmNlIHRoZSBjbHVzdGVyIHBsb3QgbG9va3MgbGlrZSBpdCBkb2VzLiBUaGUgdHdvIGRvZ2V5IHNhbXBsZXMgMTUwbUouMyBhbmQgMjc1bUouMSBoYXZlIGJlZW4gbGVmdCBvdXQgb2YgdGhlIGF2ZXJhZ2UgY29ycmVsYXRpb24gY2FsY3VsYXRpb24gYWJvdmUuCgpXb3VsZCBsaWtlIHRvIHRha2UgYSBwaWN0dXJlIG9mIHRoZSB0YWJsZSBvZiBmaW5hbCBjb25jZW50cmF0aW9ucyBvZiBzYW1wbGVzIHRoYXQgd2VudCBpbnRvIHRoZSBwb29sIGFuZCBwdXQgaXQgaW4gdGhlIHBEYXRhIGRhdGFmcmFtZSBzbyB3ZSBoYXZlIGl0IGZvciBmdXJ0dXJlIHJlZmVyZW5jZS4gUmF5bmVyIGlzIHJlLWRvaW5nIGV4cGVyaW1lbnQuIAoKVGhlIGJveHBsb3RzIHNlZW0gdG8gaW5kaWNhdGUgdGhhdCB0aGUgdHdvIGRvZGdleSBzYW1wbGVzIGFyZSBtaXNzaW5nIHByb3RlaW5zIHRoYXQgYXJlIGxvd2x5IGV4cHJlc3NlZC4gTWF5YmUgdGhleSB3ZXJlIHRvbyBsb3cgdG8gYmUgZGV0ZWN0ZWQgYnkgdGhlIG1hc3Mgc3BlYyA/IAoKYGBge3IgMDVfQ3JlYXRpbmctYS1iYWNrZ29ydW5kLXByb3RlaW4tbGlzdH0KCiMgLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLQojIFN0ZXAgMDU6IENyZWF0aW5nIGEgYmFja2dyb3VuZCBsaXN0IG9mIHByb3RlaW5zIGZvciBVMjBTIHRvIGJlIHVzZWQgZm9yIGZ1bmN0aW9uYWwgZW5yaWNobWVudAojIFVzaW5nIEdPIHRlcm1zIGFuZCBJbnRlcnBybyBkb21haW5zIGZvciBmdW5jdGlvbiBvZiBwcm90ZWlucyBkZXRlY3RlZCBieSBVViBjcm9zc2xpbmtpbmcgYW5kIFRyaXpvbC1lbnJpY2htZW50CiMgQWxsIGRhdGEgYXJlIGJhc2VkIG9uIFRyaXpvbC00LXN0ZXAgYW5kIDQwMG1KIG9mIFVWIGV4cG9zdXJlIGZvciAyIG1pbnMgKD8pCiMgLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLQoKCiM9PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT0KIyBVc2luZyBVMk9TIHByb3RlaW4gbGlzdCBmcm9tIEdlaWdlciBldCBhbCBhcyB0aGUgYmFja2dyb3VuZCBsaXN0LiBUaGlzIGNvbnNpc3RzIG9mIG1heGltYWwgbGlzdCBvZiBwcm90ZWlucyBleHByZXNzZWQgaW4gVTIwUyBjZWxsIGxpbmVzCiMgfiA3NTAwIHByb3RlaW5zLiBXZSBhcmUgaG93ZXZlciByZWFkaW5nIGluIHRoZSBwZXB0aWRlcyBhbmQgYWdncmVnYXRpbmcgdGhlbSB1c2luZyBNU25iYXNlIGFzIHdlIGRpZCB3aXRoIHRoZSBVViBkb3NhZ2UgZGF0YQojPT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09CgojIFJlYWQgZGF0YSBmcm9tIFRvbSdzIGxpc3Qgb2YgR2VpZ2VyIHByb3RlaW5zCiMgVG9tIHRvb2sgdGhlIFN1cHBsZW1lbnRhcnkgZGF0YSBmcm9tIHBhcGVyIGFuZCByZS1hbm5vdGF0ZWQgaXQgd2l0aCBtYXN0ZXIgcHJvdGVpbnMgdXNpbmcgaGlzIHNjcmlwdC4gCiMgVGhlIHNjcmlwdCBnZXRzIGZvY3VzZXMgb24gdXNpbmcgU3dpc3Nwcm90IElEcyByYXRoZXIgdGhhbiBib3RoIFN3aXNzcHJvdCBhbmQgVHJlbWJsLiAKIyBJdCBhbHNvIG1hcmtzIHdoaWNoIHByb3RlaW5zIGFyZSAiY3JhcCIgb3IgImNvbnRhbWluYW50cyIKIyBGaW5hbGx5LCBpdCBwcm92aWRlcyBhIG1lYXN1cmUgb2Ygd2hpY2ggcGVwdGlkZXMgY291bGQgYmUgbWFwcGVkIHRvIGEgdW5pcXVlIHByb3RlaW4uIFdlIHVzZSB0aGlzIGFzIGEgZmlsdGVyIGZvciBkb3duc3RyZWFtIGFuYWx5c2VzCnUyb3MgPC0gcmVhZC5kZWxpbSgiSW5wdXQvR2VpZ2VyX2V0X2FsXzIwMTJfVTJPU19wZXB0aWRlc19wbHVzX21hc3Rlci50eHQiLHNlcD0iXHQiLGhlYWRlcj1UKQpoZWFkKHUyb3MpCmRpbSh1Mm9zKSAjIDY4NjIxIDIxCgojIEZpbHRlciBkYXRhIC0ga2VlcCBwZXB0aWRlcyB3aXRoIHVuaXF1ZSBtYXN0ZXIgcHJvdGVpbnMsIHRob3NlIHdoaWNoIGFyZSBub3QgImNyYXAiIGFuZCB0aG9zZSB0aGF0IGFyZSBub3QgbWlzc2luZyBhIG1hc3RlciBwcm90ZWluCnUyb3MuZmlsdDEgPSB1Mm9zW3doaWNoKHUyb3MkdW5pcXVlID09IDEgJiB1Mm9zJG1hc3Rlcl9wcm90ZWluICE9ICIiICYgdTJvcyRjcmFwX3Byb3RlaW4gIT0gMSksXQoKIyBLZWVwIG9ubHkgdGhvc2UgY29sdW1ubnMgd2l0aCBkYXRhIG9mIGludGVyZXN0CiMgR2VpZ2VyIGV0IGFsIHByb2R1Y2VkIGEgdHJpcGxpY2F0ZSBNUyBkYXRhc2V0IGZvciB0aGUgVTJPUyBjZWxsIGxpbmUKdTJvcy5maWx0MiA9IHUyb3MuZmlsdDFbLGMoNTo2LDE3LDExOjE2LDE4OjE5KV0KaGVhZCh1Mm9zLmZpbHQyKQoKIyBDaGVja2luZyBmb3IgcGVwdGlkZSBsb3NzLiBXZSBsb29zZSB+IDQwMCBwZXB0aWRlcyBvdXQgb2YgbmVhcmx5IDcwLDAwMCBzbyBub3QgdG9vIGNvbmNlcm5lZApkaW0odTJvcykgIyA2ODYyMSAyMQpkaW0odTJvcy5maWx0MSkgIyA2NDk2NyAyMQpkaW0odTJvcy5maWx0MikgIyA2NDk2NyAxMQoKIyBDcmVhdGUgYW4gTVNuU2V0IG9iamVjdCBvZiB0aGUgYmFja2dyb3VuZCBsaXN0IAp1Mm9zLnNhbXAgPSBkYXRhLmZyYW1lKHNhbXBsZT1jKCJJbnRlbnNpdHkuVTJPU18xIiwiSW50ZW5zaXR5LlUyT1NfMiIsIkludGVuc2l0eS5VMk9TXzMiKSxyZXA9YygxLDIsMykpCnJvd25hbWVzKHUyb3Muc2FtcCkgPSB1Mm9zLnNhbXAkc2FtcGxlCnJlcy51Mm9zIDwtIE1TblNldChleHBycyA9IGFzLm1hdHJpeCh1Mm9zLmZpbHQyWyxjKDU6NyldKSxmRGF0YT11Mm9zLmZpbHQyWywtYyg1OjcpXSxwRGF0YSA9IHUyb3Muc2FtcCkKCiMgSW1wdXRlIG1pc3NpbmcgZGF0YSBpbiB0aGUgYmFja2dyb3VuZCBsaXN0IGZyb20gR2VpZ2VyIGV0IGFsLgojIEhhdmVuJ3QgZHJhd24gYW55IHBsb3RzIGZvciB0aGlzLiBDYW4gZG8gaXQgYXQgYSBsYXRlciBkYXRlCmltcHV0ZS51Mm9zIDwtIGltcHV0ZShyZXMudTJvcyxtZXRob2QgPSAia25uIikKCiMgQWdncmVnYXRlIEdlaWdlciBldCBhbCBkYXRhIGZyb20gcGVwdGlkZXMgdG8gcHJvdGVpbnMKIyA2NDk2NyBwZXB0aWRlcyBhcmUgYWdncmVnYXRlZCB0byA3NTA3IHByb3RlaW5zCmFnZy51Mm9zID0gY29tYmluZUZlYXR1cmVzKGltcHV0ZS51Mm9zLGdyb3VwQnkgPSBmRGF0YShpbXB1dGUudTJvcykkbWFzdGVyX3Byb3RlaW4sY3YgPSBULGZ1biA9ICJtZWRpYW4iKQpkaW0oYWdnLnUyb3MpCmBgYApUaGVyZSBhcmUgNzUwNyBwcm90ZWlucyBpbiB0aGUgVTIwUyBjZWxsIGxpbmUsIGFjcm9zcyAzIHJlcGxpY2F0ZXMsIGJhc2VkIG9uIHRoZSBHZWlnZXIgZXQgYWwuLCBkYXRhc2V0LiBUaGUgbmV4dCBzdGVwIGlzIHRvIGFubm90YXRlIHRoaXMgZ2VuZSBsaXN0IHdpdGggdGhlIHZhcmlvdXMgZnVuY3Rpb25hbCBjYXRlZ29yaWVzIHRoYXQgd2Ugd2FudCB0byBwZXJmb3JtIGVucmljaG1lbnQgYW5hbHlzaXMgZm9yLiAKCmBgYHtyIDA2X1UyT1MtcHJvdGVpbnMtYW5ub3RhdGlvbn0KCiMtLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0KIyBTdGVwIDA2OiBNYXBwaW5nICdiYWNrZ3JvdW5kJyBhcyB3ZWxsIGFzICdleHByZXNzZWQnIGxpc3Qgb2YgcHJvdGVpbnMgdG8gdmFyaW91cyBhbm5vdGF0aW9ucwojIFVzZWQgYSBmZXcgZGlmZmVyZW50IHBhY2thZ2VzIC0gd2FudGVkIHRvIHNldHRsZSBvbiBnZXRCTSBmcm9tICdiaW9tYVJ0JyBhcyBvdXRwdXQgaW4gZWFzeSB0byB1c2UgZm9ybWF0LiAKIyBFeGNlcHQgdGhhdCBpdCB3YXMgZXh0cmVtZWx5IHNsb3cuIEhlbmNlIHVzZWQgJ3F1ZXJ5TWFueScgZnJvbSBteWdlbmUgYW5kIHdpbGwgcmVmb3JtYXQgZGF0YS4KIy0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLQoKIy0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLQojIE1hcHBpbmcgR2VpZ2VyIFUyT1MgbGlzdCBvZiBwcm90ZWlucyB0byBHTyB0ZXJtcwojLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tCgojIEdlaWdlciBsaXN0IGJlaW5nIGFubm90YXRlZCB3aXRoIGludGVycHJvIGRlc2NyaXB0aW9ucyAtIGZhc3QgYXMgZG9uZSBpbiBjaHVua3MKIyA3NTA3IHVuaXByb3QgSURzIHlpZWxkIDc1NzQgbGluZXMgb2YgZGF0YQpsaWJyYXJ5KG15Z2VuZSkKZ2VpZ2VyLnFtID0gcXVlcnlNYW55KGZEYXRhKGFnZy51Mm9zKSRtYXN0ZXJfcHJvdGVpbixzY29wZXM9InVuaXByb3QiLGZpZWxkcz1jKCJlbnNlbWJsIiwibmFtZSIsInN5bWJvbCIsImludGVycHJvIiwiZ28iKSkKZ2VpZ2VyLnFtJGRvbWFpbnMgPSBzYXBwbHkoc2FwcGx5KGdlaWdlci5xbSRpbnRlcnBybywiW1siLDMpLGZ1bmN0aW9uKHgpIHBhc3RlKHgsY29sbGFwc2U9IjsgIikpCmdlaWdlci5xbSRnby5icCA9IHNhcHBseShzYXBwbHkoZ2VpZ2VyLnFtJGdvLkJQLCJbWyIsMiksZnVuY3Rpb24oeCkgcGFzdGUoeCxjb2xsYXBzZT0iOyAiKSkKZ2VpZ2VyLnFtJGdvLm1mID0gc2FwcGx5KHNhcHBseShnZWlnZXIucW0kZ28uTUYsIltbIiwyKSxmdW5jdGlvbih4KSBwYXN0ZSh4LGNvbGxhcHNlPSI7ICIpKQpnZWlnZXIucW0kZ28uY2MgPSBzYXBwbHkoc2FwcGx5KGdlaWdlci5xbSRnby5DQywiW1siLDIpLGZ1bmN0aW9uKHgpIHBhc3RlKHgsY29sbGFwc2U9IjsgIikpCmdlaWdlci5xbSRnby5hbGwgPSBwYXN0ZShnZWlnZXIucW0kZ28uYnAsZ2VpZ2VyLnFtJGdvLmNjLGdlaWdlci5xbSRnby5tZixzZXA9IjsgIikKI2hlYWQoZ2VpZ2VyLnFtKQoKIy0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0KIyBNYXBwaW5nIFRyaXpvbC1lbnJpY2hlZCBsaXN0IG9mIHByb3RlaW5zIHRvIGdlbmUgZG9tYWlucyBmcm9tIEludGVycHJvLgojLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLQojIDE3NDQgdW5pcHJvdCBJRHMgeWllbGQgMTc4MCBsaW5lcyBvZiBkYXRhCnV2LnFtID0gcXVlcnlNYW55KGZEYXRhKHFudC5wcm90KSRNYXN0ZXIuUHJvdGVpbi5BY2Nlc3Npb25zLHNjb3Blcz0idW5pcHJvdCIsZmllbGRzPWMoImVuc2VtYmwiLCJuYW1lIiwic3ltYm9sIiwiaW50ZXJwcm8iLCJnbyIpKQp1di5xbSRkb21haW5zID0gc2FwcGx5KHNhcHBseSh1di5xbSRpbnRlcnBybywiW1siLDMpLGZ1bmN0aW9uKHgpIHBhc3RlKHgsY29sbGFwc2U9IjsgIikpCnV2LnFtJGdvLmJwID0gc2FwcGx5KHNhcHBseSh1di5xbSRnby5CUCwiW1siLDIpLGZ1bmN0aW9uKHgpIHBhc3RlKHgsY29sbGFwc2U9IjsgIikpCnV2LnFtJGdvLm1mID0gc2FwcGx5KHNhcHBseSh1di5xbSRnby5NRiwiW1siLDIpLGZ1bmN0aW9uKHgpIHBhc3RlKHgsY29sbGFwc2U9IjsgIikpCnV2LnFtJGdvLmNjID0gc2FwcGx5KHNhcHBseSh1di5xbSRnby5DQywiW1siLDIpLGZ1bmN0aW9uKHgpIHBhc3RlKHgsY29sbGFwc2U9IjsgIikpCnV2LnFtJGdvLmFsbCA9IHBhc3RlKHV2LnFtJGdvLmJwLHV2LnFtJGdvLmNjLHV2LnFtJGdvLm1mLHNlcD0iOyAiKQojaGVhZCh1di5xbSkKCmBgYApOb3cgdGhhdCB3ZSBoYXZlIG1hcHBlZCBnZW5lcyB0byBhbm5vdGF0aW9ucywgdGltZSBmb3Igc29tZSBlbnJpY2htZW50IGFuYWx5c2lzLiAKCmBgYHtyIDA3X1BlcmZvcm1pbmctZW5yaWNobWVudC1hbmFseXNpc30KCiMtLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0KIyBTdGVwIDA3OiBQZXJmb3JtaW5nIGVucmljaG1lbnQgYW5hbHlzaXMgdXNpbmcgJ2dvc2VxJyBwYWNrYWdlCiMgV2UgdXNlIEdPIHRlcm1zIGFuZCBJbnRlcnBybyBkb21haW5zIGZvciBlbnJpY2htZW50IGFuYWx5c2lzCiMtLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0KCiMgLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tCiMgRnVuY3Rpb24gIDogcnVuR29zZXEKIyBBaW0gICAgICAgOiBydW5zIGdvc2VxIG9uIGEgbGlzdCBvZiBnZW5lcwojIElucHV0ICAgICA6IGxpc3Qgb2YgZ2VuZXMKIyBPdXRwdXQgICAgOiBlbnJpY2hlZCBsaXN0IG9mIEludGVycHJvIGRvbWFpbnMKIyAtLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0KCnJ1bkdvc2VxIDwtIGZ1bmN0aW9uKGdlbmVsaXN0LGJnbGlzdCxiaWFzLmRhdD1OVUxMLGNhdC5vbGlnbyl7CgogICMgc2V0dGluZyB1cCBnb3NlcSBvYmplY3QKICBhbGwuZ2VuZXMuY29tcCA9IHJlcCgwLG5yb3coYmdsaXN0KSkKICBuYW1lcyhhbGwuZ2VuZXMuY29tcCkgPSByb3duYW1lcyhiZ2xpc3QpCiAgYWxsLmdlbmVzLmNvbXBbd2hpY2gobmFtZXMoYWxsLmdlbmVzLmNvbXApICVpbiUgdW5pcXVlKGdlbmVsaXN0KSldID0gMQogIHRhYmxlKGFsbC5nZW5lcy5jb21wKQogIAogICMgUmVtb3ZlIG1pc3NpbmcgdmFsdWVzCiAgY29tcC5uby5taXNzaW5nID0gYWxsLmdlbmVzLmNvbXBbd2hpY2goIWlzLm5hKG5hbWVzKGFsbC5nZW5lcy5jb21wKSkpXQogIHRhYmxlKGNvbXAubm8ubWlzc2luZykKICAKICAgIAogICMgUnVubmluZyB0aGUgZnVuY3Rpb24gdG8gY2FsY3VsYXRlIHdlaWdodHMuIFdlIGhhdmUgbm8gYmlhcyBpbmZvcm1hdGlvbiBhcyB3ZSBkaWQgaW4gVVYgZXhwZXJpbWVudAogICMgVGhpcyBpcyBiZWNhdXNlIG1hc3Mgc3BlYyB3YXMgcnVuIGluIGRldGVjdGlvbiBtb2RlIG5vdCBxdWFudGl0YXRpb24gbW9kZS4gCiAgcHdmLmNvbXAgPSBudWxscChjb21wLm5vLm1pc3NpbmcsJ2hnMTknLCdnZW5lU3ltYm9sJywgYmlhcz1iaWFzLmRhdCxwbG90LmZpdD1UUlVFKQogIAogICMgZ29zZXEgZW5yaWNobWVudCB3aXRoIGRvbWFpbnMgZm9yIGNyb3NzLWxpbmtlZCBzYW1wbGVzCiAgZ29zZXEuY29tcCA9IGdvc2VxKHB3Zi5jb21wLGdlbmUyY2F0ID0gY2F0Lm9saWdvKQogIGdvc2VxLmNvbXAkQkhfb3Zlcl9yZXByZXNlbnRlZF9wdmFsdWUgPSBwLmFkanVzdChnb3NlcS5jb21wJG92ZXJfcmVwcmVzZW50ZWRfcHZhbHVlLG1ldGhvZCA9ICJCSCIpCiAgZ29zZXEuY29tcAogIAogIGVucmljaGVkLmdvc2VxLmNvbXAgPSBnb3NlcS5jb21wW3doaWNoKGdvc2VxLmNvbXAkQkhfb3Zlcl9yZXByZXNlbnRlZF9wdmFsdWUgPD0gMC4wNSksXQogIHJldHVybihsaXN0KGdvc2VxLmNvbXAsZW5yaWNoZWQuZ29zZXEuY29tcCkpCgp9CgojLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLQojIFVzaW5nIEdvc2VxIHdpdGggcHJvdGVpbiBhYnVuZGFuY2UgYXMgYSBiaWFzLi4uLgojLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLQoKIyBXYW50IHRvIGJlIGFibGUgdG8gdXNlIGJvdGggVW5pUHJvdCBhbmQgR2VuZSBzeW1ib2xzIGFzIHJlZmVyZW5jZXMgZm9yIGJpYXMgaW4gZG93bnN0cmVhbSBhbmFseXNpcwpiaWFzLmRmID0gZGF0YS5mcmFtZShwcm90YmlhcyA9IHJvd01heChleHBycyhhZ2cudTJvcykpLFVOSVBST1QgPSBmRGF0YShhZ2cudTJvcykkbWFzdGVyX3Byb3RlaW4sU1lNQk9MPXNhcHBseShzdHJzcGxpdChhcy5jaGFyYWN0ZXIoZkRhdGEoYWdnLnUyb3MpJHByb3RlaW5fZGVzY3JpcHRpb24pLCJcXHx8XFxfIiksIltbIiwzKSkKaGVhZChiaWFzLmRmKQoKIyBDb252ZXJ0aW5nIEdlaWdlciBldCBhbCBkb21haW5zIGRhdGEgdG8gYSBsaXN0IHdpdGggZWFjaCBsaW5lIG9ubHkgaGF2aW5nIG9uZSBkb21haW4KZ2VpZ2VyLmRvbXMgPSBkYXRhLmZyYW1lKGdlaWdlci5xbVssYygicXVlcnkiLCJkb21haW5zIildKQpnZWlnZXIuZ29zID0gZGF0YS5mcmFtZShnZWlnZXIucW1bLGMoInF1ZXJ5IiwiZ28uYWxsIildKQoKbGlicmFyeShkYXRhLnRhYmxlKQpkLmR0IDwtIGRhdGEudGFibGUoZ2VpZ2VyLmRvbXMsIGtleT0icXVlcnkiKQpnZWlnZXIuY2F0IDwtIGQuZHRbLCBsaXN0KGRvbWFpbnMgPSB1bmxpc3Qoc3Ryc3BsaXQoZG9tYWlucywgIjsgIikpKSwgYnk9cXVlcnldCgpnby5kdCA9IGRhdGEudGFibGUoZ2VpZ2VyLmdvcywga2V5PSJxdWVyeSIpCmdlaWdlci5jYXQuZ28gPC0gZ28uZHRbLCBsaXN0KGdvLnRlcm1zID0gdW5saXN0KHN0cnNwbGl0KGdvLmFsbCwgIjsgIikpKSwgYnk9cXVlcnldCgojIFNldHRpbmcgdXAgYSAnZ2VuZXMnIHZlY3Rvci4gV2UndmUgb25seSBrZXB0IHRob3NlIGdlbmVzIHByZXNlbnQgaW4gdTJvcyBhcyB0aGUgdW5pdmVyc2UKIyBUaGVuIHdlIG1hcmsgYWxsIHRob3NlIHRoYXQgYXJlIGVucmljaGVkIGluIHRoZSBhbmFseXNpcyBhcyBnZW5lcyBvZiBpbnRlcmVzdCAoREUgaWYgeW91IHdpbGwpCiMgV2UgaGF2ZSAxNDM1IGdlbmVzIG91dCBvZiAxNTE2IHRoYXQgaGF2ZSBiaWFzIGRhdGEgYW5kIGdvIHRlcm1zCgojIFJ1bm5pbmcgZ29zZXEgd2l0aCBHTyBjYXRlZ29yaWVzCnJvd25hbWVzKGJpYXMuZGYpID0gYmlhcy5kZiRVTklQUk9UCnV2LmVucmljaC5nbyA9IHJ1bkdvc2VxKHV2LnFtJHF1ZXJ5LGJpYXMuZGYsYmlhcy5kZiRwcm90YmlhcyxnZWlnZXIuY2F0LmdvKQojIHV2LmVucmljaC5nbyA9IHV2LmVucmljaC5nb1tvcmRlcih1di5lbnJpY2guZ28kQkhfb3Zlcl9yZXByZXNlbnRlZF9wdmFsdWUpLF0KCiMgUnVubmluZyBnb3NlcSB3aXRoIEludGVycHJvIGRvbWFpbnMKdXYuZW5yaWNoLmludGVycHJvID0gcnVuR29zZXEodXYucW0kcXVlcnksYmlhcy5kZixiaWFzLmRmJHByb3RiaWFzLGdlaWdlci5jYXQpCgojIFdyaXRpbmcgb3V0cHV0IHRvIHRleHQgZmlsZXMKd3JpdGUudGFibGUoZGF0YS5mcmFtZSh1di5lbnJpY2guZ29bWzJdXSkscGFzdGUob3V0ZGlyLCJUcml6b2wtVVYtZG9zYWdlX0dPLWVucmljaG1lbnQudHh0IixzZXA9Ii8iKSxzZXA9Ilx0Iixyb3cubmFtZXM9RixxdW90ZT1GKQp3cml0ZS50YWJsZShkYXRhLmZyYW1lKHV2LmVucmljaC5pbnRlcnByb1tbMl1dKSxwYXN0ZShvdXRkaXIsIlRyaXpvbC1VVi1kb3NhZ2VfSW50ZXJwcm8tZW5yaWNobWVudC50eHQiLHNlcD0iLyIpLHNlcD0iXHQiLHJvdy5uYW1lcz1GLHF1b3RlPUYpCmBgYApUaGUgcHJvdGVpbiAidW5pdmVyc2UiIHVzZWQgaGVyZSBpcyB0aGUgbGlzdCBvZiBhbGwgcHJvdGVpbnMgZGlzY292ZXJlZCBpbiBHZWlnZXIgZXQgYWwuLCAyMDEyLiBUaGUgbGlzdCBvZiBwZXB0aWRlcyB3YXMgbWFwcGVkIHRvIHByb3RlaW5zIGJ5IFRvbSBhbmQgYW5ub3RhdGVkIHdpdGggbWFzdGVyIHByb3RlaW4gaWRlbnRpZmllcnMsIGNyYXAgcHJvdGVpbnMgYW5kIHVuaXF1ZW5lc3MuIFRoaXMgd2FzIHVzZWQgYXMgaXQgaXMgdGhlIG1vc3QgY29tcHJlaGVuc2l2ZSBsaXN0IG9mIFUyT1MgcHJvdGVpbnMgbWFwcGVkIHVzaW5nIG1hc3Mgc3BlYyB0byBkYXRlLiBBZnRlciBmaWx0ZXJpbmcsIHRoZXJlIHdlcmUgfjc1MDAgcHJvdGVpbnMgZ2VuZXJhdGVkIGluIHRoZSBzdHVkeS4gCgpUaGUgIkRFIGxpc3QiIG9yICJlbnJpY2hlZCIgbGlzdCBvZiBwcm90ZWlucyBhcmUgdGhvc2UgdGhhdCB3ZXJlIGV4cHJlc3NlZCBpbiB0aGUgVVYgZG9zYWdlIGV4cGVyaW1lbnQgYWNyb3NzIGFsbCA5IHNhbXBsZXMuIFRoaXMgeWllbGRzIDE3NDQgcHJvdGVpbnMgb2Ygd2hpY2ggfjE1MDAgY291bGQgYmUgbWFwcGVkIHRvIEdPIGlkZW50aWZpZXJzLiBJIHRyaWVkIGRvaW5nIHRoaXMgbWFwcGluZyB3aXRoIGJvdGggJ2Jpb01hcnQnIGFuZCAnTXlHZW5lJyBkYXRhYmFzZXMgYXZhaWxhYmxlIGFzIHBhY2thZ2VzIHdpdGhpbiBSLiBUaGUgbGF0dGVyIHdhcyBwaGVub21lbmFsbHkgZmFzdGVyLCBoZW5jZSBJIHByb2NlZWRlZCB3aXRoIGl0LiAKCkZvciBHTyBlbnJpY2htZW50LCBJIHVzZWQgdGhlICdnb3NlcScgcGFja2FnZSB3aGljaCBhY2NvdW50cyBmb3IgYW55IGJpYXMgKGhlcmUgYWJ1bmRhbmNlIG9mIHByb3RlaW4pIGFuZCB0aGVuIGNoZWNrcyBmb3IgZW5yaWNobWVudC4gJ2dvc2VxJyB5aWVsZGVkIDk0IHRlcm1zIG9mIHdoaWNoIDQwIHdlcmUgQlAsIDM2IHdlcmUgQ0MgYW5kIDE4IHdlcmUgTUYgdGVybXMuIE9mIHRoZSBCUCAoQmlvbG9naWNhbCBQcm9jZXNzKSB0ZXJtcywgbW9yZSB0aGFuIGhhbGYgd2VyZSBpbnZvbHZlZCBpbiBSTkEgcHJvY2Vzc2luZyBhbmQgdHJhbnNsYXRpb24uCgpXb3VsZCBsaWtlIHRvIG1hcCBlYWNoIG9mIHRoZSBwcm90ZWlucyB0byBQRkFNL1NNQVJUIGRvbWFpbnMgdG8gc2VlIGlmIHRoZXkgYXJlIGluZGVlZCBSTkEgYmluZGluZyBwcm90ZWlucy4uLi4uLgpIYXZlIG1hcHBlZCBSTkEgQlAgZG9tYWlucyB0byBib3RoIEdlaWdlciBhbmQgVVYuZG9zYWdlLiAKTmVlZCB0byBsb29rIGF0IHRoZSBnZW5lcyBpbnZvbHZlZCBmb3Igd2hpY2ggd2UgaGF2ZSBmdXJ0aGVyIGV2aWRlbmNlIHRoYXQgdGhleSBhcmUgaW5kZWVkIFJCUHMKClRoZSBkb21haW4gIk51Y2xlb3RpZGUtYmRfYS9iX3BsYWl0IiBpcyBwcmVzZW50IGluIGFsbW9zdCBhbGwgKDE2LzE4KSBoZXRlcm9nZW5lb3VzIG51Y2xlYXIgcmlib251Y2xlb3Byb3RlaW5zIHdob3NlIG1haW4gdGFzayBpcyB0byBtb3ZlIG1STkEgb3V0IG9mIHRoZSBudWNsZXVzLiBUaGlzIGRvbWFpbiBoYXMgYW4gaW50ZXJwcm8gZW50cnkgSVBSMDEyNjc3IHdoaWNoIGlzIG5vIGxvbmdlciB2YWxpZC4gVGhpcyBkb21haW4gaXMgYWxzbyBwcmVzZW50IGluIG51Y2xlb2xpbiBhbmQgRVdTUjEuIFRoaXMgbWlnaHQgYmUgdGhlICJSR0ctYm94IiBkb21haW4gZWx1ZGVkIHRvIGluIEJ1cmQgYW5kIERyZXlmdXNzLCAxOTk0LiBFeGNpdGluZ2x5LCAiTnVjbGVvdGlkZS1iZF9hL2JfcGxhaXQiIGlzIGEgdG9wIGRvbWFpbiB0aGUgZ29zZXEgYW5hbHlzaXMuIEkgd2lsbCBzdWJzdGl0dXRlIGhuUk5QIHNlYXJjaGVzIHdpdGggdGhpcyB0ZXJtLiAKCiJJZy1saWtlIi8iSWdfc3ViIiBmcm9tIFRvbSdzIG5vdGVzIGFyZSBpbmRpY2F0aXZlIG9mIGdseWNvcHJvdGVpbnMgd2hpY2ggbWlnaHQgYmUgUk5BLWJpbmRpbmcuIFRoZXJlIGFyZSA1OCBJZyB0ZXJtIHJlbGF0ZWQgcHJvdGVpbnMgaW4gdGhlIFVWLWRvc2FnZSBleHBlcmltZW50cyBvZiB3aGljaCBvbmx5IDIgaGF2ZSBSTkEtYmluZGluZyBkb21haW5zIGluZGljYXRpbmcgb25seSBhIHNtYWxsIGZyYWN0aW9uIGhhdmUgUk5BIGJpbmRpbmcgY2FwYXBiaWxpdGllcyBidXQgbWFqb3JpdHkgb2YgdGhlbSBhcmUgaW52b2x2ZWQgaW4gb3RoZXIgZnVuY3Rpb25zLgoKYGBge3IgMDhfTWFwcGluZy1vbGlnb2RULXRvLWRvbWFpbnN9CgojLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0KIyAwOCA6IE1hcHBpbmcgb2xpZ29kVCBkYXRhIHRvIGRvbWFpbnMgYW5kIGxvb2tpbmcgZm9yIGVucmljaG1lbnQKIy0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tCgpvbGlnby5jbC5pbi5uYyA9IGFzLmNoYXJhY3RlcihyZWFkUkRTKCJJbnB1dC9DTF9wcm90ZWlucy5yZHMiKSkgIyA2NTIgdW5pcXVlIFN3aXNzcHJvdCBJRHMgZnJvbSBUb20ncyBwZXB0aWRlIGFnZ3JlZ2F0ZWQgYW5kIHByb3RlaW4gcmVuYW1lZCBmaWxlCiNvbGlnby5jbC5pbi5uYyA9IHJlYWQudGFibGUoIklucHV0L0xlaWNlc3Rlci1vbGlnb2RULUNMLXByb3QudHh0IixoZWFkZXI9VCxzZXA9Ilx0IikgIyAzMjggZ2VuZSBzeW1ib2xzIGZyb20gUmF5bmVyJ3MgZmlsZQoKb2xpZ28ubmMgPSBhcy5jaGFyYWN0ZXIocmVhZFJEUygiSW5wdXQvTkNMX3Byb3RlaW5zLnJkcyIpKSAjIDE3NSB1bmlxdWUgU3dpc3Nwcm90IElEcyBmcm9tIFRvbSdzIHBlcHRpZGUgYWdncmVnYXRlZCBhbmQgcHJvdGVpbiByZW5hbWVkIGZpbGUKI29saWdvLm5jID0gcmVhZC50YWJsZSgiSW5wdXQvTGVpY2VzdGVyLW9saWdvZFQtTkMtcHJvdC50eHQiLGhlYWRlcj1ULHNlcD0iXHQiKSAjIDczIGdlbmUgc3ltYm9scyBmcm9tIFJheW5lcidzIGZpbGUKCmxlbmd0aChvbGlnby5jbC5pbi5uYykKbGVuZ3RoKG9saWdvLm5jKQoKIyBQcmVzZW50IGluIG5vbi1jcm9zc2xpbmtlZCBhbmQgaGVuY2Ugd2lsbCBiZSByZW1vdmVkCgpvbGlnby5jbCA9IG9saWdvLmNsLmluLm5jWy13aGljaChvbGlnby5jbC5pbi5uYyAlaW4lIG9saWdvLm5jKV0gCmxlbmd0aChvbGlnby5jbCkgIyA0ODkgdW5pcXVlIFN3aXNzcHJvdCBJRHMgZnJvbSBUb20ncyBmaWxlcyBub3QgcHJlc2VudCBpbiBub24tY3Jvc3NsaW5rZWQgc2FtcGxlcwoKIyBXaGljaCBnZW5lcyBhcmUgcHJlc2VudCBpbiBib3RoIGNyb3NzIGFuZCBub24tY3Jvc3NsaW5rZWQgc2FtcGxlcwpjbC5hbmQubmMucHJvdCA9IG9saWdvLmNsLmluLm5jW3doaWNoKG9saWdvLmNsLmluLm5jICVpbiUgb2xpZ28ubmMpXQpsZW5ndGgoY2wuYW5kLm5jLnByb3QpICMgMTYzIHByZXNlbnQgaW4gYm90aCBjcm9zc2xpbmtlZCBhbmQgbm9uLWNyb3NzbGlua2VkIHNhbXBsZXMKCiMgQ29udmVydCBkYXRhIHRhYmxlCiMgZ2VpZ2VyLmRvbXMuc3ltID0gZGF0YS5mcmFtZShnZWlnZXIucW1bLGMoInN5bWJvbCIsImRvbWFpbnMiKV0pCiMgZ2VpZ2VyLmdvcy5zeW0gPSBkYXRhLmZyYW1lKGdlaWdlci5xbVssYygic3ltYm9sIiwiZ28uYWxsIildKQoKIyBkLmR0LnN5bSA8LSBkYXRhLnRhYmxlKGdlaWdlci5kb21zLnN5bSwga2V5PSJzeW1ib2wiKQojIGdlaWdlci5jYXQuc3ltIDwtIGQuZHQuc3ltWywgbGlzdChkb21haW5zID0gdW5saXN0KHN0cnNwbGl0KGRvbWFpbnMsICI7ICIpKSksIGJ5PXN5bWJvbF0KCiMgZ28uZHQuc3ltID0gZGF0YS50YWJsZShnZWlnZXIuZ29zLnN5bSwga2V5PSJzeW1ib2wiKQojIGdlaWdlci5jYXQuZ28uc3ltIDwtIGdvLmR0LnN5bVssIGxpc3QoZ28udGVybXMgPSB1bmxpc3Qoc3Ryc3BsaXQoZ28uYWxsLCAiOyAiKSkpLCBieT1zeW1ib2xdCgojIFNldHRpbmcgdXAgYSAnZ2VuZXMnIHZlY3Rvci4gV2UndmUgb25seSBrZXB0IHRob3NlIGdlbmVzIHByZXNlbnQgaW4gdTJvcyBhcyB0aGUgdW5pdmVyc2UKIyBUaGVuIHdlIG1hcmsgYWxsIHRob3NlIHRoYXQgYXJlIGVucmljaGVkIGluIHRoZSBhbmFseXNpcyBhcyBnZW5lcyBvZiBpbnRlcmVzdCAoREUgaWYgeW91IHdpbGwpCiMgV2UgaGF2ZSAxNDM1IGdlbmVzIG91dCBvZiAxNTE2IHRoYXQgaGF2ZSBiaWFzIGRhdGEgYW5kIGdvIHRlcm1zCgojIFJ1bm5pbmcgZ29zZXEgZm9yIG9saWdvZFQgd2hpY2ggaGF2ZSBnZW5lIHN5bWJvbHMgYXMgaWRlbnRpZmllcnMKIyByb3duYW1lcyhiaWFzLmRmKSA9IGJpYXMuZGYkU1lNQk9MCgojIENyb3NzbGlua2VkIG9saWdvZFQgYWxsIHByb3RlaW5zCmNsLmFsbC5lbnJpY2guZ28gPSBydW5Hb3NlcShvbGlnby5jbC5pbi5uYyxiaWFzLmRmLGJpYXMuZGYkcHJvdGJpYXMsZ2VpZ2VyLmNhdC5nbykKY2wuYWxsLmVucmljaC5pbnRlcnBybyA9IHJ1bkdvc2VxKG9saWdvLmNsLmluLm5jLGJpYXMuZGYsYmlhcy5kZiRwcm90YmlhcyxnZWlnZXIuY2F0KQp3cml0ZS50YWJsZShjbC5hbGwuZW5yaWNoLmdvW1syXV1bLGMoMSw2OjcsNDo1LDIsOCldLHBhc3RlKG91dGRpciwib2xpZ29kVC1jcm9zc2xpbmtlZC13aXRoLW5jLXByb3RzX0dPLWVucmljaG1lbnQudHh0IixzZXA9Ii8iKSxzZXA9Ilx0Iixyb3cubmFtZXM9RixxdW90ZT1GKQp3cml0ZS50YWJsZShjbC5hbGwuZW5yaWNoLmludGVycHJvW1syXV0scGFzdGUob3V0ZGlyLCJvbGlnb2RULWNyb3NzbGlua2VkLXdpdGgtbmMtcHJvdHNfSW50ZXJwcm8tZW5yaWNobWVudC50eHQiLHNlcD0iLyIpLHNlcD0iXHQiLHJvdy5uYW1lcz1GLHF1b3RlPUYpCgojIENyb3NzbGlua2VkIG9saWdvZFQgbWludXMgcHJvdGVpbnMgaW4gbm9uLWNyb3NzbGluZWQgc2FtcGxlcwpjbC5lbnJpY2guZ28gPSBydW5Hb3NlcShvbGlnby5jbCxiaWFzLmRmLGJpYXMuZGYkcHJvdGJpYXMsZ2VpZ2VyLmNhdC5nbykKY2wuZW5yaWNoLmludGVycHJvID0gcnVuR29zZXEob2xpZ28uY2wsYmlhcy5kZixiaWFzLmRmJHByb3RiaWFzLGdlaWdlci5jYXQpCndyaXRlLnRhYmxlKGNsLmVucmljaC5nb1tbMl1dWyxjKDEsNjo3LDQ6NSwyLDgpXSxwYXN0ZShvdXRkaXIsIm9saWdvZFQtY3Jvc3NsaW5rZWRfR08tZW5yaWNobWVudC50eHQiLHNlcD0iLyIpLHNlcD0iXHQiLHJvdy5uYW1lcz1GLHF1b3RlPUYpCndyaXRlLnRhYmxlKGNsLmVucmljaC5pbnRlcnByb1tbMl1dLHBhc3RlKG91dGRpciwib2xpZ29kVC1jcm9zc2xpbmtlZF9JbnRlcnByby1lbnJpY2htZW50LnR4dCIsc2VwPSIvIiksc2VwPSJcdCIscm93Lm5hbWVzPUYscXVvdGU9RikKCiMgTm9uLWNyb3NzbGlua2VkIHNhbXBsZXMKbmMuZW5yaWNoLmdvID0gcnVuR29zZXEob2xpZ28ubmMsYmlhcy5kZixiaWFzLmRmJHByb3RiaWFzLGdlaWdlci5jYXQuZ28pCm5jLmVucmljaC5pbnRlcnBybyA9IHJ1bkdvc2VxKG9saWdvLm5jLGJpYXMuZGYsYmlhcy5kZiRwcm90YmlhcyxnZWlnZXIuY2F0KQp3cml0ZS50YWJsZShuYy5lbnJpY2guZ29bWzJdXVssYygxLDY6Nyw0OjUsMiw4KV0scGFzdGUob3V0ZGlyLCJvbGlnb2RULW5vbi1jcm9zc2xpbmtlZF9HTy1lbnJpY2htZW50LnR4dCIsc2VwPSIvIiksc2VwPSJcdCIscm93Lm5hbWVzPUYscXVvdGU9RikKd3JpdGUudGFibGUobmMuZW5yaWNoLmludGVycHJvW1syXV0scGFzdGUob3V0ZGlyLCJvbGlnb2RULW5vbi1jcm9zc2xpbmtlZF9JbnRlcnByby1lbnJpY2htZW50LnR4dCIsc2VwPSIvIiksc2VwPSJcdCIscm93Lm5hbWVzPUYscXVvdGU9RikKCiMgQ29tbW9uIHRvIGJvdGggY3Jvc3MtbGlua2VkIGFuZCBub24tY3Jvc3NsaW5rZWQgc2FtcGxlcwpuYy5jbC5lbnJpY2guZ28gPSBydW5Hb3NlcShjbC5hbmQubmMucHJvdCxiaWFzLmRmLGJpYXMuZGYkcHJvdGJpYXMsZ2VpZ2VyLmNhdC5nbykKbmMuY2wuZW5yaWNoLmludGVycHJvID0gcnVuR29zZXEoY2wuYW5kLm5jLnByb3QsYmlhcy5kZixiaWFzLmRmJHByb3RiaWFzLGdlaWdlci5jYXQpCndyaXRlLnRhYmxlKG5jLmVucmljaC5nb1tbMl1dWyxjKDEsNjo3LDQ6NSwyLDgpXSxwYXN0ZShvdXRkaXIsIm9saWdvZFQtY2wtYW5kLW5jX0dPLWVucmljaG1lbnQudHh0IixzZXA9Ii8iKSxzZXA9Ilx0Iixyb3cubmFtZXM9RixxdW90ZT1GKQp3cml0ZS50YWJsZShuYy5lbnJpY2guaW50ZXJwcm9bWzJdXSxwYXN0ZShvdXRkaXIsIm9saWdvZFQtY2wtYW5kLW5jX0ludGVycHJvLWVucmljaG1lbnQudHh0IixzZXA9Ii8iKSxzZXA9Ilx0Iixyb3cubmFtZXM9RixxdW90ZT1GKQoKCmBgYApJbiB0aGlzIGZpcnN0IHN0ZXAsIHdlIGFyZSByZWFkaW5nIGluIG9saWdvZFQgZGF0YSBmcm9tIG9uZSBleHBlcmltZW50IHdpdGggYm90aCBjcm9zc2xpbmtlZChjbCkgYW5kIG5vbi1jcm9zc2xpbmtlZChuYykgc2FtcGxlcy4gV2UgcmVtb3ZlIHByb3RlaW5zIGZyb20gdGhlICdjbCcgd2hpY2ggd2VyZSBhbHNvIHByZXNlbnQgaW4gJ25jJyBhcyB3ZSBjYW5ub3QgY29tbWVudCBvbiBlbnJpY2htZW50LgoKSW50ZXJlc3RpbmdseSwgdGhlIG9saWdvZFQgZGF0YSBzZWVtcyBhIGxvdCAiY2xlYW5lciIgdGhhbiB0cml6b2wgZGF0YXNldC4gQnkgdGhpcyBJIG1lYW4sIHRoZSB0b3AgZG9tYWlucyBhcmUgbW9zdCBkZWZpbml0ZWx5IGFsbCBrbm93biBSTkEtYmluZGluZyBkb21haW5zIGJhc2VkIG9uIGxpdGVyYXR1cmUgbG9va2luZyBhdCBSQlBzIChMdW5kZSAyMDA3LCBCdXJkIDE5OTQpLiBJbiB0aGUgVHJpem9sIGRhdGEgd2UgZ2V0ICJJZy1saWtlIiBkb21haW5zIGFzIG9uZSBvZiB0aGUgdG9wIGhpdHMgd2hpY2ggd2UgZG9uJ3Qgc2VlIGF0IGFsbCBpbiB0aGUgb2xpZ29kVCBkYXRhLgoKQmV0d2VlbiBjcm9zc2xpbmtlZCBhbmQgbm9uLWNyb3NzbGlua2VkIHNhbXBsZXMsIHdlIHN0aWxsIHNlZSBzb21lIG92ZXJsYXAgaW4gdGhhdCB3ZSBhcmUgZ2V0dGluZyBSTkEtYmluZGluZyBkb21haW5zIGFuZCBwcm90ZWlucyBpbiBub24tY3Jvc3NsaW5rZWQgc2FtcGxlcyBidXQgdGhlIG51bWJlciBvZiBzdWNoIHByb3RlaW5zIGluIGEgTE9UIGxvd2VyIGluIG5vbi1jcm9zc2xpbmtlZCB0aGFuIGluIGNyb3NzbGlua2VkIHNhbXBsZXMuIAoKYGBge3IgMDlfQW5ub3RhdGluZy1SQkQtZG9tYWluc30KIy0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLQojIFN0ZXAgMDkgOiBBbm5vdGF0aW5nIGFsbCBwcm90ZWluIGxpc3RzIHdpdGggY291bnRzIG9mIFJCRCBkb21haW5zCiMtLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0KCiMtLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLQojIE1ha2luZyBhIGxpc3Qgb2YgZG9tYWlucyB0aGF0IGRlZmluZSBSTkEgYmluZGluZyBwcm90ZWlucwojIENvdWxkbid0IGZpbmQgUElXSS9QQVogZG9tYWlucyBpbiB0aGUgVVYgZ2VuZSBsaXN0IGJ1dCBwcmVzZW50IGluIEdlaWdlciBsaXN0CiMgTG9va2luZyBhdCBCdXJkIHBhcGVyIHdoaWNoIGluY2x1ZGVzICdSR0cnIGJveCB3aGljaCBJIHRoaW5rIGlzIEhuUk5QIGFzIHdlbGwgYXMgY29sZF9zaG9jayBkb21haW4KIyBDYW4ndCBmaW5kIFRSQVAgcHJvdGVpbiAodHJwIFJOQS1CaW5kaW5nIGF0dGVudWF0aW9uIHByb3RlaW4pCiMgSGF2ZSBhZGRlZCBuZXcgUkJEcyBmcm9tIENhc3RlbGxvJ3MgMjAxNiBwYXBlciAtIFRoaW9yZWRveGluLCBQRFosIERaRiwgCiMtLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLQoKcmJkLmRvbXMgPSBjKCJSUk1fZG9tIiwiS0hfZG9tIiwiZHNSQkRfZG9tIiwiWm5mX0NDQ0giLCJabmZfQzJIMiIsIlpuZl9DQ0hDIiwiUzFfZG9tIiwiUEFaX2RvbSIsIlBpd2kiLCJOdWNsZW90aWRlLWJkX2EvYl9wbGFpdCIsIkNTRCIsIkNvbGQtc2hvY2tfQ1MiLCJQdW1pbGlvX1JOQS1iZF9ycHQiLCJTQU07IiwiU0FNL3BvaW50ZWQiLCJERUFEIiwiVGhpb3JlZG94aW4iLCJQRFoiLCJEWkZfZG9tIikKcmJkLmRvbXMKCiMgQW5ub3RhdGluZyBib3RoIGdlaWdlciBhbmQgdXYtZG9zYWdlIGRhdGFzZXRzIHdpdGgga25vd24gInJiZCIgZG9tYWlucyBhbmQgaG93IG1hbnkgb2YgdGhlc2UgUkJEIGRvbWFpbnMgZWFjaCBwcm90ZWluIGhhcy4KdXYucW0kbnVtLnJiZHMgPSByb3dTdW1zKHNhcHBseShyYmQuZG9tcywgZnVuY3Rpb24oeCkgZ3JlcGwoeCwgdXYucW0kZG9tYWlucykpKQp1di5xbSR3aGljaC5yYmQgPSBhcHBseShzYXBwbHkocmJkLmRvbXMsIGZ1bmN0aW9uKHgpIGdyZXBsKHgsIHV2LnFtJGRvbWFpbnMpKSwxLCBmdW5jdGlvbih5KSBwYXN0ZShuYW1lcyh3aGljaCh5PT1UKSksY29sbGFwc2U9IjsgIikpCiNoZWFkKHV2LnFtW3doaWNoKHV2LnFtJG51bS5yYmRzICE9IDApLF0sbj0xMCkKCmdlaWdlci5xbSRudW0ucmJkcyA9IHJvd1N1bXMoc2FwcGx5KHJiZC5kb21zLCBmdW5jdGlvbih4KSBncmVwbCh4LCBnZWlnZXIucW0kZG9tYWlucykpKQpnZWlnZXIucW0kd2hpY2gucmJkID0gYXBwbHkoc2FwcGx5KHJiZC5kb21zLCBmdW5jdGlvbih4KSBncmVwbCh4LCBnZWlnZXIucW0kZG9tYWlucykpLDEsIGZ1bmN0aW9uKHkpIHBhc3RlKG5hbWVzKHdoaWNoKHk9PVQpKSxjb2xsYXBzZT0iOyAiKSkKI2hlYWQoZ2VpZ2VyLnFtW3doaWNoKGdlaWdlci5xbSRudW0ucmJkcyAhPSAwKSxdLG49MTApCgoKIyBQcmludGluZyB3aGF0IHBlcmNlbnRhZ2UgIG9mIGVhY2ggbGlzdCBpcyBhbm5vdGF0ZWQgd2l0aCBSTkEgYmluZGluZyBkb21haW5zCm9iai5uYW1lcyA9IGMoIm9saWdvLmNsLmluLm5jIiwib2xpZ28uY2wiLCJvbGlnby5uYyIsImNsLmFuZC5uYy5wcm90IikKY291bnQgPSAwCgpmb3IoeSBpbiBsaXN0KG9saWdvLmNsLmluLm5jLG9saWdvLmNsLG9saWdvLm5jLGNsLmFuZC5uYy5wcm90KSl7CiAgY291bnQgPSBjb3VudCsxCiAgeSA9IHF1ZXJ5TWFueSh5LHNjb3Blcz0idW5pcHJvdCIsZmllbGRzPWMoImVuc2VtYmwiLCJuYW1lIiwic3ltYm9sIiwiaW50ZXJwcm8iLCJnbyIpKQogIHkkZG9tYWlucyA9IHNhcHBseShzYXBwbHkoeSRpbnRlcnBybywiW1siLDMpLGZ1bmN0aW9uKHgpIHBhc3RlKHgsY29sbGFwc2U9IjsgIikpCiAgeSRudW0ucmJkcyA9IHJvd1N1bXMoc2FwcGx5KHJiZC5kb21zLCBmdW5jdGlvbih4KSBncmVwbCh4LCB5JGRvbWFpbnMpKSkKICB5JHdoaWNoLnJiZCA9IGFwcGx5KHNhcHBseShyYmQuZG9tcywgZnVuY3Rpb24oeCkgZ3JlcGwoeCwgeSRkb21haW5zKSksMSwgZnVuY3Rpb24oeikgcGFzdGUobmFtZXMod2hpY2goej09VCkpLGNvbGxhcHNlPSI7ICIpKQogIHByaW50KHRhYmxlKHkkbnVtLnJiZHMpKQogIHByaW50KHBhc3RlKCJQZXJjZW50YWdlIG9mIHByb3RlaW5zIHdpdGggUk5BLWJpbmRpbmcgZG9tYWlucyBpbiAiLCBvYmoubmFtZXNbY291bnRdLCIgPSAiLHJvdW5kKDEwMCpzdW0odGFibGUoeSRudW0ucmJkcylbMjo0XSkvc3VtKHRhYmxlKHkkbnVtLnJiZHMpKSwyKSxzZXA9IiIpKQp9CmBgYAoKYGBge3IgMTBfSW50ZXJzZWN0LW9mLVRyaXpvbC1vbGlnb2RUfQojLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0KIyAxMCA6IExvb2tpbmcgYXQgdGhlIGludGVyc2VjdCBvZiBwcm90ZWlucyBiZXR3ZWVuIG9saWdvZFQgYW5kIFRyaXpvbAojLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0KCiMgVXNpbmcgVHJpem9sIG1hcHBlZCB0byBoZ25jX3N5bWJvbCBhcyBvbGlnb2RUIGlzIG9ubHkgaW4gc3ltYm9scy4KbGlicmFyeSh2ZW5uKQpUcml6b2wgPSB1bmlxdWUodXYucW0kcXVlcnkpCm9saWdvZFQgPSB1bmlxdWUob2xpZ28uY2wpCnYgPSB2ZW5uKGxpc3QoVHJpem9sPVRyaXpvbCxvbGlnb2RUPW9saWdvZFQpLGludGVyc2VjdGlvbnM9VCkKCmJvdGggPSBhdHRyKHYsImludGVyc2VjdGlvbiIpJGBUcml6b2w6b2xpZ29kVGAgIyBuID0gMjk4Cm9saWdvLm9ubHkgPSBhdHRyKHYsImludGVyc2VjdGlvbiIpJGBvbGlnb2RUYCAjIG4gPSAxOTEKdHJpem9sLm9ubHkgPSBhdHRyKHYsImludGVyc2VjdGlvbiIpJGBUcml6b2xgICMgbiA9IDE0NDYKCiMgRW5yaWNobWVudCBmb3Igb3ZlcmxhcHMgYW5kIHNldGRpZmZzCm0gPSBjKCJib3RoIiwib2xpZ28tb25seSIsInRyaXpvbC1vbmx5IikKYyA9IDAKZm9yKHQgaW4gbGlzdChib3RoPWJvdGgsb2xpZ28ub25seT1vbGlnby5vbmx5LHRyaXpvbC5vbmx5PXRyaXpvbC5vbmx5KSl7CiAgYz1jKzEKICB0LmVucmljaC5nbyA9IHJ1bkdvc2VxKHQsYmlhcy5kZiwgYmlhcy5kZiRwcm90YmlhcyxnZWlnZXIuY2F0LmdvKQogIHQuZW5yaWNoLmludGVycHJvID0gcnVuR29zZXEodCxiaWFzLmRmLCBiaWFzLmRmJHByb3RiaWFzLGdlaWdlci5jYXQpCiAgd3JpdGUudGFibGUodC5lbnJpY2guZ29bWzJdXVssYygxLDY6Nyw0OjUsMiw4KV0scGFzdGUob3V0ZGlyLHBhc3RlKG1bY10sIi1nZW5lcy1HTy1lbnJpY2htZW50LnR4dCIsc2VwPSIiKSxzZXA9Ii8iKSxzZXA9Ilx0Iixyb3cubmFtZXM9RixxdW90ZT1GKQogIHdyaXRlLnRhYmxlKHQuZW5yaWNoLmludGVycHJvW1syXV0scGFzdGUob3V0ZGlyLCBwYXN0ZShtW2NdLCItZ2VuZXMtSW50ZXJwcm8tZW5yaWNobWVudC50eHQiLHNlcD0iIiksc2VwPSIvIiksc2VwPSJcdCIscm93Lm5hbWVzPUYscXVvdGU9RikKfQoKb28gPSBpbnRlcnNlY3Qob2xpZ28ub25seSxnZWlnZXIucW0kcXVlcnkpICMgMTcwLzE5MQp0dCA9IGludGVyc2VjdCh0cml6b2wub25seSxnZWlnZXIucW0kcXVlcnkpICMgMTE0NC8xNDQ2CmJsID0gaW50ZXJzZWN0KGJvdGgsZ2VpZ2VyLnFtJHF1ZXJ5KSAjIDI5Ny8yOTggbWlzaW5nIEVJRjNDL1E5OTYxMwpgYGAKCmBgYHtyIDExX0RldG91ci1jb21wYXJpbmctb3RoZXItVTJPUy1kYXRhc2V0c30KCiMtLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0KIyBTdGVwIDExIDogQ29tcGFyaW5nIDMgZGlmZmVyZW50IFUyT1MgcHJvdGVvbWljcyBkYXRhc2V0cyBmcm9tIHRoZSBjdXJyZW50IGRlY2FkZQojLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tCgpnZWlnZXIgPSBhcy5jaGFyYWN0ZXIodW5pcXVlKGZEYXRhKGFnZy51Mm9zKSRtYXN0ZXJfcHJvdGVpbikpCgpiZWNrID0gcmVhZC50YWJsZSgiSW5wdXQvQmVjazIwMTFfbjU3ODEudHh0IikKYmVjayA9IGFzLmNoYXJhY3RlcihiZWNrJFYxKQoKbHVuZGJlcmcuZW5zID0gcmVhZC50YWJsZSgiSW5wdXQvTHVuZGJlcmcyMDEwX241NDgwLnR4dCIpCmx1bmRiZXJnID0gdW5pcXVlKGJpdHIoYXMuY2hhcmFjdGVyKGx1bmRiZXJnLmVucyRWMSksZnJvbVR5cGU9IkVOU0VNQkwiLCB0b1R5cGU9IlVOSVBST1QiLCBPcmdEYj0ib3JnLkhzLmVnLmRiIikkVU5JUFJPVCkKbGVuZ3RoKGx1bmRiZXJnKQoKbGlicmFyeSh2ZW5uKQpsaWJyYXJ5KGdwbG90cykKbGlicmFyeShsaW1tYSkKCnZlbm4udTJvcyA9IHZlbm4obGlzdChHZWlnZXIyMDEyPWdlaWdlcixCZWNrMjAxMT1iZWNrLEx1bmRiZXJnMjAxMD1sdW5kYmVyZykpCgpgYGAKU21hbGwgZXhlcmNpc2Ugb24gaG93IG11Y2ggR2VpZ2VyIGV0IGFsLCBCZWNrIGV0IGFsLCBhbmQgTHVuZGJlcmcgZXQgYWwuLCBwcm90ZWluIHNldHMgZnJvbSBNYXNzIFNwZWN0cm9tZXRlcnkgZXhwZXJpbWVudHMgb3ZlcmxhcC4gV2l0aCBMdW5kYmVyZyBldCBhbC4sIEkgaGFkIHRvIG1hcCBFbnNlbWJsIElEcyB0byBVTklQUk9UIGFuZCB0aGVuIGRvIHRoZSBjb21wYXJpc29uIHdoaWNoIGNhdXNlZCBhIG9uZS10by1tYW55IG1hcHBpbmcuIFRoZSBleGNlc3MgNDk0MSBvbmx5IGZvdW5kIGluIEx1bmRiZXJnIGV0IGFsIGlzIGFsbW9zdCBleGNsdXNpdmVseSBkdWUgdG8gdGhlIG1hcHBpbmcgb2Ygb25lIEVuc2VtYmwgSUQgdG8gbWFueSBVTklQUk9UIElEcy4gR2VpZ2VyIGV0IGFsLCBiZWluZyB0aGUgbW9zdCByZWNlbnQgc3R1ZHkgZG9lcyBjbGFpbSB0byBoYXZlIG1heGltYWwgcHJvdGVpbiBtYXBwaW5nIGZvciBVMk9TLiBCZWNrIGV0IGFsLiwgCgpTaG91bGQgd2UgdXNlIGp1c3QgdGhlIDQxNDUgdGhhdCBvdmVybGFwcyBhcyBvdXIgaGlnaCBjb25maWRlbnQgc2V0IG9yIGZvY3VzIG9uIEdlaWdlciBhcyBpdCB0aGUgbW9zdCByZWNlbnQgPyAKCg==